tag:blogger.com,1999:blog-52214658923827133952024-03-14T00:04:42.573-07:00The Software SimpletonPaul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.comBlogger51125tag:blogger.com,1999:blog-5221465892382713395.post-24361357084213666132011-09-07T13:58:00.001-07:002011-09-07T13:58:01.957-07:00This Blog Has Moved<p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="t4_x" style="text-align:left"><br></div><div id="x65j" style="text-align:left">I have finally had enough of blogger and moved my blog <a href="http://thesoftwaresimpleton.com/" id="x_3h" title="http://thesoftwaresimpleton.com">here</a>.</div><div id="qcf3" style="text-align:left"><br></div><div id="eh0q" style="text-align:left"><div id="udor" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_481cqhpvkhg_b" style="height:153.42056074766356px;width:648px"></div><br></div><div id="kr_:" style="text-align:left"><br></div><div id="b-82" style="text-align:left">Click on the link on the top right of the new site to subscribe via feed burner:</div><div id="w9dh" style="text-align:left"><br></div><div id="zc3u" style="text-align:left">I hope you will continue to follow me as I continue on my war against terror.</div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-61561224703768323922011-06-14T08:24:00.001-07:002011-06-14T12:17:44.161-07:00WINRM - Loosely Translated as SSH for Windows<p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="t4_x" style="text-align:left">When I am developing a side project, I always make sure it is on a different operating system and a different language than what I would normally use in my day job. I want there to be a clear distinction or else the lines can become all too blurred. My <a href="http://www.leadcapturer.com/" id="eylp" title="current">current</a> side project is written in ruby and rails and it is while developing this that I have fallen in love with both <a href="http://en.wikipedia.org/wiki/Secure_Shell" id="gt19" title="SSH">SSH</a> and <a href="http://wiki.rubyonrails.org/deployment/capistrano" id="r6xy" title="Capistrano">Capistrano</a>. SSH or secure shell is a protocol for remote administration of Unix computers. Simply put, SSH allows me to stay in the same terminal instance when I am moving from my client machine to other remote servers. <br><br>Capistrano is a DSL for deploying ruby on rails applications. Capistrano has highlighted just what a gaping hole this is in current ugly .NET deployment practices. I very much include my own crude powershell scripts in this dirty breed. Capistrano uses SSH to connect to the remote server and run your deployment from your client machine. Pretty nifty and yet another source of rails envy I now have.<br><br>Returning to my day job and windows, I missed this facility and started looking at powershell for a similar capability. There is more than one way of executing powershell on a remote server. <a href="http://msdn.microsoft.com/en-us/library/aa384426(v=vs.85).aspx" id="kjie" title="Winrm">Winrm</a> looked to be the best fit for me. With winrm, you can start and finish powershell sessions on the remote server. In the examples I am about to give, all the machines have powershell 2.0 installed. There are different requirements for each operating system. The following <a href="http://msdn.microsoft.com/en-us/library/aa384372(v=vs.85).aspx" id="l.7g" title="guide">guide</a> should help you install winrm on the client and on any remote servers you wish to access.<br><h1>Enabling Winrm on a remote Server for Client Requests</h1>Enabling WinrRM between computers on the same domain is very easy, simply run the following command in an elevated Powershell console on the remote server. <br><br></div><div id="x65j" style="text-align:left"><font style="background-color:#000000"><font color="#ff0000">PS></font></font><font style="background-color:#000000"><font color="#ffffff"> Enable-PSRemoting <br></font></font><br>You should get a response like this:<br><br><div id="fv2i" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_478gs5mvwf7_b" style="height:230px;width:1284px"></div><br></div><div id="qcf3" style="text-align:left"><br>If you do not get a response like above you might want to check that winrm is installed and that the windows service is running.<br><br>By default winrm runs over http on port 5985 and you might need your network guys to open a hole in the firewall for this port. It took the network guys I work with five weeks to accomplish this task which is an absurd amount of time for such a task but I better not get into that now. We also restricted access to this port to an I.P. range for extra security. <br><br>If the server is on a different domain, you also need to run the following command which sets a registry entry that allows a client server to authenticate from a different domain to the local server.<br><br><font size="3"><font style="background-color:#000000"><font color="#ff0000">PS></font><font color="#ffffff"> New-Itemproperty -name LocalAccountTokenFilterPolicy -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -propertyType DWord -value 1</font><br></font></font><br>Another gotcha that I fell for is to make sure that you restart the winrm windows service to pick up the changes. <br><h1>Enabling Winrm on the Client</h1>You might not need to do this but I had to add a trust entry for every server I wanted to connect to with the following command on my workstation:</div><div id="eh0q" style="text-align:left"><br></div><div id="kr_:" style="text-align:left"><span style="font-style:normal"><font style="background-color:#000000"><font face="tahoma"><font color="#ff0000" size="3">PS></font><font color="#ffffff" size="3"> winrm s winrm/config/client '@{TrustedHosts="samedomainserver,99.999.999.999"}' <br></font></font></font></span><br></div><div id="b-82" style="text-align:left">You will note that I have more than one server listed here and they are comma delimited. I also have a computer name and an I.P. address in my list. The IP address is for a server on a different domain.<br><h1>Executing Commands on the Remote Server</h1>There are two ways that I know of executing commands on the remote server. The first is by invoking the <a href="http://technet.microsoft.com/en-us/library/dd347578.aspx" id="na7z" title="Invoke-Command">Invoke-Command</a> cmdlet which allows you to access one off commands:<br><br></div><div id="w9dh" style="text-align:left"><font style="background-color:#000000"><font color="#ff0000">PS></font><font color="#ffffff"> Invoke-Command -ComputerName MyRemoteServer -ScriptBlock {Get-Process} -Credential Get-Credential <br></font></font><br>The <a href="http://technet.microsoft.com/en-us/library/dd315327.aspx" id="k6df" title="Get-Credential">Get-Credential</a> cmdlet opens a windows form dialog to allow you to log in against the required account:<br><br><div id="upze" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_479f3697v86_b" style="height:311.57260273972605px;width:648px"></div><br></div><div id="zc3u" style="text-align:left"><br>The second allows you to start a remote powershell session using the <a href="http://technet.microsoft.com/en-us/library/dd315384.aspx" id="gsyz" title="Enter-PSsession">Enter-PSsession</a> cmdlet:<br><br><font color="#ff0000"><font style="background-color:#000000">PS></font></font><font color="#ffffff"><font style="background-color:#000000"> Enter-Pssession -ComputerName MyRemoteServer -Credential Get-Credential </font></font> <br><br>I can also exit my remote session with the <a href="http://technet.microsoft.com/en-us/library/dd315322.aspx" id="md4k" title="Exit-PSsession">Exit-PSsession</a> cmdlet. <br><h1>The Icing on the Winrm Cake</h1>I talked about the powershell profile in <a href="http://thesoftwaresimpleton.blogspot.com/2011/05/own-your-powershell-profile.html" id="n.27" title="this">this</a> post and starting a remote powershell session is a prime candidate for adding to my powershell profile. I have the following two functions defined in my powershell profile:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">function PS-Production</font></li><li style="background:#f3f3f3"><font size="2">{</font></li><li><font size="2"> Enter-PSSession -ComputerName 99.999.999.999 -Credential Get-Credential</font></li><li style="background:#f3f3f3"><font size="2">}</font></li><li> </li><li style="background:#f3f3f3"><font size="2">function PS-Demo</font></li><li><font size="2">{</font></li><li style="background:#f3f3f3"><font size="2"> Enter-PSSession MyRemoteServer<font size="2"> -Credential Get-Credential</font></font></li><li><font size="2">}</font></li></ol></div></div><br>It can get confusing when bouncing about from server to server so I have the following function in my profile to let me know exactly which account I am running under:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">function whoami </font></li><li style="background:#f3f3f3"><font size="2">{ </font></li><li><font size="2"> (get-content env:\userdomain) + "\" + (get-content env:\username); </font></li><li style="background:#f3f3f3"><font size="2">}</font></li></ol></div></div><br>If you are on windows and still flapping about with Dos or worse, using windows explorer to do everything then I beseige you to pick up powershell. It is worth the effort.<br><br>I think a capistrano like experience could be created with powershell and winrm. Maybe something I will look into at some stage.<br><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com1tag:blogger.com,1999:blog-5221465892382713395.post-38643845618147527622011-06-07T08:58:00.001-07:002011-06-07T09:07:08.735-07:00Gracefully handling Nil and Empty in Ruby<p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="wc0c" style="text-align:left">One of the nice side effects of extension methods in .NET is that you can easily handle nulls without littering your code with null checks. <br><br>For example if you look at the following extension method that I have defined in C#:</div><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2"> <font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">bool</font> HasElements<T>(<font color="#0000ff">this</font> <font color="#2b91af">IEnumerable</font><T> collection)</font></li><li style="background:#f3f3f3"><font size="2"> {</font></li><li><font size="2"> <font color="#0000ff">return</font> collection != <font color="#0000ff">null</font> && collection.Count() != 0;</font></li><li style="background:#f3f3f3"><font size="2"> }</font></li></ol></div></div><br>I can then use this method on any object that <i>is</i> an IEnumerable of T:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2"> <font color="#0000ff">if</font> (trainingEvent.Contacts.HasElements())</font></li><li style="background:#f3f3f3"><font size="2"> {</font></li><li><font size="2"> //do something</font></li><li style="background:#f3f3f3"><font size="2"> }</font></li></ol></div></div><br>Which is much easier on the eye than:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2"> <font color="#0000ff">if</font> (trainingEvent.Contacts != <font color="#0000ff">null</font> && trainingEvent.Contacts.Count > 0)</font></li><li style="background:#f3f3f3"><font size="2"> {</font></li><li> <font size="2">//do something</font></li><li style="background:#f3f3f3"><font size="2"> }</font></li></ol></div></div><br>The reason this is possible is because an extension method in .NET is really a bit of syntactic sugar that effectively rearranges the method into a static method call, which might be equivalent to this:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2"> <font color="#0000ff">if</font> (<font color="#2b91af">EnumerableExtensions</font>.HasElements(<font size="2">trainingEvent.Contacts</font>))</font></li><li style="background:#f3f3f3"><font size="2"> {</font></li><li><font size="2"> // do something</font></li><li style="background:#f3f3f3"><font size="2"> }</font></li></ol></div></div><br>It is all done with compiler magic.<br><br>When coding in Ruby, I found myself wanting the same experience. All too often, I found myself writing code like this:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">if !lead.address.nil? && !lead.address.empty?</font></li></ol></div></div><br><div>or this:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">if lead.contacts.nil? || lead.contacts.empty?</font></li></ol></div></div></div><br>In ruby everything is an object even Nil (null in .NETspeak) and everything is open for extension. You just redefine the class and add your own customisations. I actually thought about extending Nil myself before telling myself that was very hacky. I then found out that this is exactly what happens in the core extensions of the activesupport module.<br><br>In this set of extensions, the authors have extended a number of the core objects to add support for a <b>blank?</b> method. According to the documentation:<br><br><i>An object is blank when it's false, empty or a whitespace string</i>. <br><br>All that I need to do is add the following line to my <b>~/config/initializers/requires.rb</b> file and I get the <b>blank?</b> extension method over the whole project. <br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">require 'active_support/core_ext/object'</font></li></ol></div></div><br>I can then use <b>blank?</b> with strings:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">if !address.blank?</font></li></ol></div></div><br><div>Or with arrays:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">if lead.contacts.blank?</font></li></ol></div></div></div><br>That will take care of nil, empty strings, and arrays with no elements.<br><br>I get the same result for arrays, booleans and hashes.<br><br>So how is this achieved? A quick look at the source reveals that some of the core objects are extended (hence the name stupid!). <br><br>For example the Nil class is extended to always return true for a call to blank?:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">class NilClass #:nodoc:</font></li><li style="background:#f3f3f3"><font size="2"> def blank?</font></li><li><font size="2"> true</font></li><li style="background:#f3f3f3"><font size="2"> end</font></li><li><font size="2">end</font></li></ol></div></div><br>This will obviously elegantly handle all our <b>nil?</b> cases in one full swoop. The code then opens up several other classes for extension and adds the <b>blank?</b> method. <br><br>For example Array is opened and a nice <a href="http://ruby.about.com/od/rubyfeatures/a/aliasing.htm" id="i_lz" title="alias">alias</a> is added to enable a call to blank? to respond with empty?<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">class Array #:nodoc:</font></li><li style="background:#f3f3f3"><font size="2"> alias_method :blank?, :empty?</font></li><li><font size="2">end</font></li></ol></div></div><br><div>The Sting class is nicely extended with the ruby <i>not matches operator</i> on a simple regex:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">class String #:nodoc:</font></li><li style="background:#f3f3f3"><font size="2"> def blank?</font></li><li><font size="2"> self !~ /\S/</font></li><li style="background:#f3f3f3"><font size="2"> end</font></li><li><font size="2">end</font></li></ol></div></div></div><br>But the nice bit is the extension that is added to the Object class:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">class Object</font></li><li style="background:#f3f3f3"><font size="2"> def blank?</font></li><li><font size="2"> respond_to?(:empty?) ? empty? : !self</font></li><li style="background:#f3f3f3"><font size="2"> end</font></li><li><font size="2">end</font></li></ol></div></div><br>The respond_to? method simply checks whether the object you are sending a blank? message to has such a method.<br><br>There are other useful extensions in this module that you can find out about <a href="http://edgeguides.rubyonrails.org/active_support_core_extensions.html" id="uvp4" title="here">here</a>.<br><br>I think this is quite nice and has tidied up my code a lot. <br><br><br>If you are a seasoned Ruby developer, you probably knew this and have not bothered reading this far.<br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com2tag:blogger.com,1999:blog-5221465892382713395.post-67129411241193916952011-05-22T01:11:00.001-07:002011-05-22T01:24:13.427-07:00Own Your Powershell Profile<p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="t4_x" style="text-align:left">I have not blogged in quite a while but I think it is worth giving powershell a mention. I think that a lot of windows developers are missing a trick by first of all not using powershell and secondly by not customising their powershell profile. <br><br>Like a lot of windows developers, I had never really seen the need for powershell and I had written it off without even trying it. I happily fumbled about with DOS and batch files. That is until I bought a Mac Book air and decided that the bash shell was a much more agreeable experience compared to messing about with the Mac windows explorer equivalent called finder. I was instantly struck how much more efficient using the terminal (command prompt in windows speak) is than messing about with windows. The obvious advantage being that you can create reusable scripts of the recurring actions you frequently execute during the course of your terminal session. In bash you can configure your shell by placing customisation commands in 1 of 3 system configuration files, the one I used most was the .<b>bashrc </b>file that is processed every time you open a non-login shell. One of the most productive customisations that I discovered was the ability to set up typing short cuts or <b><i>aliases</i></b>.<br><br>Once I returned to windows, I found myself loathe to use windows explorer, filezilla and other graphical user interfaces. I was struck by how inefficient they were and how I was repeating the same actions over and over again. The whole point of programming is to automate repeating tasks. I then turned to powershell and I was delighted to discover that powershell has the concept of the powershell profile which is analogous to the .<b>bashrc </b>file in that it is processed every time you open a powershell shell command prompt. <br><h1>Your Divine Right To Customise Your Shell</h1>The first task that you that you need to accomplish, that is if you have yet to customise your profile is to find the damn thing. Luckily powershell holds the location in a built in global variable that you can access by typing <b><i>$PROFILE</i></b> at your powershell command prompt:</div><div id="x65j" style="text-align:left"><div id="zorb" style="text-align:left"><br></div><div id="qcf3" style="text-align:left"><font style="background-color:#000000"><font color="#ffffff">PS> $PROFILE </font></font> </div><div id="eh0q" style="text-align:left"><br></div><div id="kr_:" style="text-align:left"><b><i>$PROFILE</i></b> returns the full path of the file that powershell will try to run when it starts<br></div><div id="b-82" style="text-align:left"> </div><div id="w9dh" style="text-align:left"><font style="background-color:#000000"><font color="#ffffff">PS> C:\users\paul.cowan\Documents\windowsPowerShell\Microsof.Powershell_profile.ps1 </font></font></div><br>Strangely, the file is not created for you by default. You can of course use powershell to test for its existence using the <b><i>Test-Path</i></b> cmdlet:<br><div id="zc3u" style="text-align:left"><br></div><div id="v46x" style="text-align:left"><font color="#ffffff"><font style="background-color:#000000">PS> Test-Path $profile </font></font></div><div id="qr4h" style="text-align:left"><br></div><div id="uh0j" style="text-align:left">If your profile does not exist, you need to create it (obviously) and the <b><i>New-Item</i></b> cmdlet will enable you to achieve this</div><div id="yapa" style="text-align:left"><br><font color="#ffffff"><font style="background-color:#000000">PS> New-Item -path $profile -type -file -force </font></font></div><br></div><div id="eok0" style="text-align:left">Once you have created the <b><i>$profile</i></b>, you can set to work customising it by opening the file in notepad:<br><br><font color="#ffffff"><font style="background-color:#000000">PS> notepad $profile </font></font><br><h1>Aliases</h1>I mentioned aliases at the start of this post and this is where we can start to add typing shortcuts to drastically speed up our workflow. We can for example add an alias that will open up our favourite text editor. This is accomplished with the <b><i>set-alias</i></b> cmdlet:<br><br></div><div id="xsd:" style="text-align:left"><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">set-alias npp "C:\Program Files (x86)\Notepad++\notepad++.exe"</font></li></ol></div></div><br>If we type the above entry into our powershell profile file (Microsoft.PowerShell_profile.ps1) and then try to run the new command by entering npp into an <b><i>existing </i></b>powershell command prompt, we will get an error along the lines of</div><div id="ujye" style="text-align:left"><br></div><div id="bvw1" style="text-align:left"><b>The term 'npp' is not recognised as the name of a cmdlet, function or operable program.</b></div><div id="ba.z" style="text-align:left"><br></div><div id="grhi" style="text-align:left">This is because the profile is only processed at the beginning of each new powershell prompt session. We can though reload the profile using the <b><i>dot source</i></b> syntax below or by opening a new powershell command prompt:<br><br><font color="#ffffff"><font style="background-color:#000000">PS> . $PROFILE </font></font></div><div id="peq9" style="text-align:left"><br></div><div id="kqq5" style="text-align:left">Now if we type npp into the prompt, the notepad++ executable will start.<br><h1>Functions</h1>Being able to add your own functions to your powershell profile and have them available for every powershell session is insanely useful. This is best illustrated with a very simple example. I have a number of functions and aliases that let me teleport from location to location on the file system. The below example will <b><i>cd</i></b> into my projects directory:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">function pr</font></li><li style="background:#f3f3f3"><font size="2">{</font></li><li><font size="2"> set-location C:\projects\ </font></li><li><font size="2">}</font></li></ol></div></div><br></div><div id="k00c" style="text-align:left"><font style="background-color:#000000"><font color="#ffffff">PS> pr </font></font> </div><div id="vh6j" style="text-align:left"><br>Two keystrokes will get me where I want to go. You really need to contrast this puerile example with the same actions in windows explorer.<br><br></div><div id="h-4v" style="text-align:left">This is ridiculously simple but I have lots of these simple location changers that zip me about from place to place.</div><div id="jv1n" style="text-align:left"><br></div><div id="l97p" style="text-align:left">Below is a powershell function that I use constantly when finding files:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">function ff ([string] $glob) </font></li><li style="background:#f3f3f3"><font size="2">{ </font></li><li><font size="2"> get-childitem -recurse -include $glob </font></li><li style="background:#f3f3f3"><font size="2">}</font></li></ol></div></div><br></div><div id="d0xh" style="text-align:left">If I want to recursively find all text files in a directory hive, I can simply type the following into the prompt:<br><br><font style="background-color:#000000"><font color="#ffffff">PS> ff *.txt </font></font></div><div id="apas" style="text-align:left"><br></div><div id="lzk3" style="text-align:left">Once you start adding these customisations, you will find yourself constantly tweaking your workflow.</div><div id="o.f." style="text-align:left"><h1>Combining Functions and Aliases</h1>The following example shows how I can create a powershell remote session on another server. I am not going to get into the syntax here but I might blog about remote powershell sessions in another post which at this rate will be in another year. The point of this example is to show how I can create the session with 2 key strokes. First of all is the function to create the session:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">function New-PSSecureRemoteSession</font></li><li style="background:#f3f3f3"><font size="2">{</font></li><li><font size="2"> param ($sshServerName, $Cred)</font></li><li style="background:#f3f3f3"><font size="2"> $Session = New-PSSession $sshServerName -UseSSL -Credential $Cred -ConfigurationName C2Remote</font></li><li><font size="2"> Enter-PSSession -Session $Session</font></li><li style="background:#f3f3f3"><font size="2">}</font></li></ol></div></div><div id="cf5e" style="text-align:left"><br></div>I then set an alias to the function:<br><br><div style="color:#000000"><div style="background-color:#dddddd;background-image:initial"><ol style="background-color:#ffffff;background-image:initial;margin-left:2em;margin-right:0px"><li><font size="2">set-alias sh New-PSRemoteSession</font></li></ol></div></div><br>Two keystrokes and I have a new session on the server. I hope this illustrates what is truly possible as you become more comfortable. I was inspired to look for the ability to execute remote powershell commands after having this ability with ssh on unix and linux. I would never have even considered this if I had not looked into other platforms which really is the moral of this story. You should always look to other platforms to bring ingenuity into your own.<br></div><h1>The Icing on the cake</h1><div id="nxf9" style="text-align:left">As you build up your profile over time, it becomes indispensable. You want to have it with you at all times. I keep mine in git and I can simply clone it onto any new machine that I am working on. You could also use dropbox.<br><br>I think I read in the <b><i>Pragmatic Programmer</i></b> that you should know your shell and as usual, this is sound advice.<br><br>Here is a link to my entire <a href="https://gist.github.com/978564" id="wext" title="profile">profile</a>.</div><div id="s5y5" style="text-align:left"><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com4tag:blogger.com,1999:blog-5221465892382713395.post-7401230123296176792010-11-22T01:38:00.001-08:002010-11-22T01:47:28.860-08:00Creating dynamic methods with closures in Ruby<p style="margin:0cm 0cm 10pt"></p><div style="background-color:transparent;font-family:'Times New Roman';margin-left:0px;margin-right:0px"><span id="yln1" style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">I came across an interesting problem while working on my yet to be birthed Microsisv project</font></font></font></span><a href="http://www.leadcapturer.com/"> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000099"><u><font size="3">leadcapture</font></u></font></font></span></a><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">r. I want to document this discovery before I forget all about it. My product, leadcapturer will scrape company and lead details from directory websites to provide fresh leads for marketing departments. In order to help me achieve this difficult goal, I have been using the totally awesome</font></font></font></span><a href="http://nokogiri.org/"> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000099"><u><font size="3">Nokogiri</font></u></font></font></span></a> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">which is a wrapper around the C based libxml libraries. WIth Nokogiri’s assistance, I can perform</font></font></font></span><a href="http://www.w3.org/TR/xpath/"> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000099"><u><font size="3">xpath</font></u></font></font></span></a> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">on html documents even if the documents are invalid xml which let us face facts, most html documents are. <br class="kix-line-break"><br class="kix-line-break">Which brings me to the point of this post. I wanted to use a regular expression in an xpath expression to return all text nodes that match the regex pattern. This is not possible in normal xpath but is possible with the help of Nokogiri. The nokogiri documentation provides the code listed below as an example:<br class="kix-line-break"><br class="kix-line-break"></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 1</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> node.xpath('.//title[regex(., "\w+")]', Class.new {</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 2</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> def regex(node_set, regex)</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 3</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 4</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> end</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 5</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> }.new)<br class="kix-line-break"><br class="kix-line-break"></font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">In the above example, a new instance method named </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>regex </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">is created within an anonymous class defined by Class.new. This ruby instance method is used as a predicate for the xpath expression. A predicate is a Boolean expression which in an xpath context will return all nodes that are true for the boolean function defined within the square brackets. In the above example, we want all nodes with an attribute of </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>some_attribute</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"> that match the regex “\w+”.<br class="kix-line-break"><br class="kix-line-break">The problem was that I wanted to make use of closures to call methods on the </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>outer</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"> class that defines the </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>Class.new</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"> anonymous class. When I mention closure in this context, I specifically mean being able to refer to variables from the context in which the closure was created. The variable I wanted to make use of in this case was the </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>self </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">of the outer class that defines the anonymous class. In Ruby methods are not closures, only blocks are. I could not use </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>self </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">in the Class.new </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>regex </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">instance method because </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>self </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">in this context would refer to the anonymous class and not the containing class.<br class="kix-line-break"><br class="kix-line-break">The answer to this puzzle was to use </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>define_method </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">which allows an author to dynamically create a new method on an object. </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>define_method </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">takes a method or a block as an argument. As I stated earlier, methods are not closures in Ruby but blocks are. As I can pass a block to </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>define_method</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">, my problem was solved, here is a stripped down version of the end result:<br class="kix-line-break"><br class="kix-line-break"></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 1</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> lead = self</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 2</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> expression = /\w+/</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 3</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> parent.xpath("./descendant::text()[regex(.)]", Class.new{</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 4</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> define_method(:regex) do |node_set|</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 5</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> result = node_set.find_all do |node|</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 6</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> if node.text =~ expression</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 7</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> lead.attribute = node.text</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 8</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> return true</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 9</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> end</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 10</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> false</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 11</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> end</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 12</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> end</font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#2b91af"><font style="background-color:#ffffff"><font size="2"> 13</font></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="'courier new'"><font color="#000000"><font style="background-color:#ffffff"><font size="2"> }.new)<br class="kix-line-break"></font></font></font></font></span><font size="3"><br></font><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">In line 1, I am binding the containing or outer class to a variable named </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>lead </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">which means I can call methods of the outer class in the anonymous inner class defined by Class.new.<br class="kix-line-break"><br class="kix-line-break">In line 4, I am using </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>define_method</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"> to dynamically create a method named </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>regex </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">and also pass a block as an argument to </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>define_method</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"> that will become the dynamically created instance method’s method body.<br class="kix-line-break"><br class="kix-line-break">In line 7 I am able to call a method (</font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>attribute=</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">) of the outer class by using the variable I bound to </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>self </b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">in line 1.<br class="kix-line-break"><br class="kix-line-break">I think this is pretty cool and one of the reasons why I am really enjoying the different dynamic paradigms available in Ruby. I also think that this is a more readable alternative that can be used instead of the “magic” of </font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3"><b>method_missing</b></font></font></font></span><span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">.</font></font></font></span> <span style="font-style:normal;vertical-align:baseline"><font face="arial"><font color="#000000"><font size="3">Depending on your circumstances of course.</font></font></font></span></div><b><br></b><p></p><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com2tag:blogger.com,1999:blog-5221465892382713395.post-73852722902731774972010-08-06T00:51:00.001-07:002010-08-06T00:56:00.164-07:00The Venusian Landscape of Ruby Metaprogramming<div><span style="font-style:normal"><font face="arial"><font color="#000000">In this post, I am going to resist the urge to rant about the stench of hackery that both WebMatrix and Microsoft.Data.dll has brought forth and instead use this post to solidify some Ruby idioms in my head. I use this this blog as either a cathartic vehicle for me to vent my frustrations or when I blog more technically, it is a way for me to cement in my head new concepts that I am likely to forget. </font></font></span></div><br><div>I want to know what tools are available to me in the dynamic land of Ruby and not code my applications like I would in the more familiar land of C#. Of course I might end up using them just for the sake of it but it is useful to know none the less.</div><br><div><span style="font-style:normal"><font face="arial"><font color="#000000">As I, the author am still very new to this brave new world of Ruby and if you think that I am blogging out my ass then please feel free to write a rude comment telling me how wrong I am. God knows I have done this enough times myself so maybe a bit of payback will not do me any harm.</font></font></span></div><br><div><span style="font-style:normal"><font face="arial"><font color="#000000">As I write more Ruby and dig more into Rails, I am utterly blown away by just how abstract and surreal the language is. To me, some of the dynamic language constructs are akin to watching a David Lynch film while on a heady cocktail of hallucinogenics.<br><br>In Ruby, there really are some very odd concepts and idioms that I see repeated over and over in the source code that I have been reading in my quest to get up to speed with Ruby.</font></font></span></div><br><div><span style="font-style:normal"><font face="arial"><font color="#000000">If you have used the Ruby on Rails Default ORM ActiveRecord, you will be very familiar with the syntax below:<br></font></font></span></div><p style="margin:0px"></p><div style="margin:0px"><br><div style="background:white;color:black;font-family:Courier New"><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><font size="4"><font color="#2b91af"> 1</font> class ExpenseType < ActiveRecord::Base</font></p><p style="margin:0px"><font size="4"><font color="#2b91af"> 2</font> has_many :expenses</font></p><p style="margin:0px"><font size="4"><font color="#2b91af"> 3</font> end</font></p></div><br><span style="font-style:normal"><font face="arial"><font color="#000000">These method calls are also known as class macros and are the ActiveRecord association class macros that define relationships between entities in much the same way as the <b><one-to-many/></b> and <b><many-to-one/></b> type elements do in Nhibernate mapping files.</font></font></span></div></div><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"> </span></p></div><div><span style="font-style:normal"><span style="font-style:normal"><font face="arial"><font color="#000000">I have been writing these methods for a while but I really did not have much of a clue what was going on. There really is quite a lot of metaprogramming magic going on behind the scenes which is not initially obvious. As the ActiveRecord source code is the installation when you install the gem, I dug deep into the crazy, crazy code.<br></font></font></span></span><br></div><div><span style="font-style:normal"><span style="font-style:normal"><font face="arial"><font color="#000000">It is rather odd to see these association defining method calls dangling outside of a method definition and instead hanging on the class itself. It is now that I will breach the first weird concept. The <b><i>has_many</i></b> class macro method above is not actually an instance method, it is actually what is known as a class method.</font></font></span></span></div><br><div><span style="font-style:normal"><span style="font-style:normal"><font face="arial"><font color="#000000">This is where things start getting very surreal so let us see how long you stick around before navigating away to some dull and predictable .NET related DDD post like "<i>Obsessive use of Aggregate roots with absolutely no Mutators enables your projects to ship on time</i>.".</font></font></span></span></div><br><div><span style="font-style:normal"><span style="font-style:normal"><font face="arial"><font color="#000000">Class methods as it turns out are very odd, they are members of what is often known as the singleton class or even more weirdly, the Eigenclass. I still have doubts whether singleton classes and Eigenclasses are the same thing but I think they are.<br><br>Time for an example before I try to explain any more:<br><br></font></font></span></span></div><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 1</font> duck = "Daffy Duck"</font></span></span></p><p style="margin:0px"><span style="font-style:normal"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 2</font> </font></span></span></p><p style="margin:0px"><span style="font-style:normal"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 3</font> def duck.speak</font></span></span></p><p style="margin:0px"><span style="font-style:normal"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 4</font> puts "That's all folks"</font></span></span></p><p style="margin:0px"><span style="font-style:normal"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 5</font> end</font></span></span></p></div><div><span style="font-style:normal"> </span></div><div><span style="font-style:normal">Here the <b>speak</b> method is certainly not part of the String class. No other string responds to this method. The <b>speak</b> definition above creates a method that only exists for a single object, not for all classes of that object. While most object oriented languages have class structures that support both instance methods and class methods (often known as static methods), Ruby only supports instance methods. If Ruby only supports instance methods, where does the <b>speak</b> method end up? On the singleton or Eigenclass of course.<br><br>This is possible because Ruby classes are actually objects instantiated from the <b><i>Class</i></b> class. That is right, the <b><i>Class</i></b> class, you heard me right. The Class of an object in Ruby is an object instance itself. Time for another example to flesh this out:<br><br></span></div><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 1 </font>class Duck</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 2</font> def self.waddle() </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 3</font> puts "wibble wobble"</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 4</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 5</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 6</font> class << self</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 7</font> def swim()</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 8</font> puts "we are swimming"</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 9</font> end </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 10</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 11</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 12</font> def procreate()</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 13</font> puts "do you come here often"</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 14</font> end </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 15</font> end</font></span></p></div><br><span style="font-style:normal">Both <b>waddle </b>and <b>swim</b> are both class methods with swim defined by a different syntax that <b>opens up</b> the singleton class for extension. <b>procreate</b> is an instance method that behaves as you would expect. <br><br>Confused? I know I have been. <br><br>I could probably spend the whole post writing about this mysterious concept but I now want to get back to the code that is woven from the <b><i>has_many</i></b> class method.<br><br>Below is a class I am going to use to illustrate just about all the Ruby metaprogramming constructs that I have leaned by this stage so I am going deliberately over the top here.. <br><br></span><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 1</font> class ClassWithExpensiveMethod</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 2</font> include CacheMixins</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 3</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 4</font> def long_method #expensive method call</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 5</font> sleep 2 </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 6</font> "result"</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 7</font> end </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 8</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 9</font> cache_result :long_method</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 10</font> end</font></span></p></div><br><br><div><span style="font-style:normal">You can see we have a rather badly named class called <b><i>ClassWithExpensiveMethod</i></b> which has a class method similar to the <i><b>has_mas_many</b> </i>class method called <b><i>cache_result</i></b>. cache_result passes as an argument, the name of any method calls it wants to......cache the result of.<br><br>What we want this method to do is call any methods it takes as an argument (the <b><i>long_result</i></b> instance method in this case) the first time it is called to get the result of the invocation but after that we want to cache the result as the call is expensive.<br><br></span></div><div><span style="font-style:normal">I am going to illustrate a lot of Ruby dynamic concepts to show how this is achieved but it is suffice to say that I would not go to this much trouble in reality for something so futile. <br><br>You can see in line 2 of the above code that we are <b>including</b> a module called <b>CacheMixins</b> which will Mixin behaviour to any class that includes it, I mentioned mixins in my previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/07/ditching-service-layer-and-ioc-for.html" id="zkux" title="post">post</a>.<br><br>Here is the implementation of CacheMixins which is <b><i>mixed in</i></b> to the above class:<br><br></span></div><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 1</font> module CacheMixins</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 2</font> def self.included(base)</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 3</font> base.extend(ClassMethods)</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 4</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 5</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 6</font> module ClassMethods</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 7</font> def cache_result(name)</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 8</font> real_method = "_real_#{name}"</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 9</font> alias_method :"#{real_method}", name</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 10</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 11</font> define_method name do</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 12</font> cache = instance_variable_get("@#{name}")</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 13</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 14</font> if cache</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 15</font> return cache</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 16</font> else</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 17</font> result = send(real_method)</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 18</font> instance_variable_set("@#{name}", result)</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 19</font> return result</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 20</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 21</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 22</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 23</font> end</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 24</font> end</font></span></p></div><br><br><span style="font-style:normal">There are a lot of concepts so I will take them one at a time.<br><br>On line 2, we are overriding the base class method <b><i>included</i></b> which is one of the methods that are known as a <i><b>Hook Method</b>.</i> Ruby provides a number of hooks that cover events in the object model. Here we are overriding the included hook method which fires whenever the module that overrides the method is included by a class. You can see in line 2 of ClassWithExpensiveMethod that we have the <b><i>include</i></b> method call and the name of the module that will be mixed in. Whenever the include method is called, the <b><i>included</i></b> method is fired and the including class (ClassWIthExpensiveMethod) is passed as an argument to the hook method.<br><br>In the <b><i>included</i></b> method, the including class which can </span><span style="font-style:normal">often be known as the inclusor (ClassWithExpensiveMethod in this instance) has class methods mixed in to it via the <b><i>extend</i></b> method. The <b><i>extend</i></b> method mixes in behaviour to the singleton class or Eigenclass we mentioned previously. The class methods are defined in a nested module rather unoriginally called ClassMethods. It turns out that this is a popular Ruby idiom and you do see it quite a lot or more than once anyway. Naming the inner module ClassMethods is just a convention and is not a keyword or anything like that.<br><br>Still with me or are you reading about the importance of the ubiquitous language in one of the plague of DDD posts that I am trying to weed out of my RSS reader? <br><br>OK, onto the actual implementation of <b>cache_result</b>.<br><br>We stated earlier that we want to execute the method call the first time to get the result and then return the cached result thereafter and I will now explain how <b><i>cache_result</i></b> is achieving this.<br><br>In lines 8 and 9 of <b>cache_result</b> we are using the aliasing feature of Ruby whereby you can give an alternate name to a Ruby method. Here we are redefining the <b><i>long_method</i></b> method of ClassWithExpensiveMethod which is passed to <b><i>cache_result</i></b> by the method call <b>cache_result :long_method. <br></b><br>Using aliases, we are redefining <b><i>long_method</i></b> with the alias <b><i>_real_long_method </i></b>(line 8). The alias now refers to the the original method <b><i>long_method</i></b>. When you redefine a method like this, you do not really change the method. Instead you define a new method and attach an existing name to the new method. On line 11 we are using one of Ruby's dynamic powers by using <b><i>define_method</i></b> to create a class method on the included class. <b><i>define_method</i> </b>takes a name for the new method and a block for the functionality.<br><br>You can see here we are defining a new method with the name we just aliased. This allows us to wrap a sort of AOP style functionality around the original method. This technique is often called an <b><i>around alias</i></b> and is another idiom you see quite a lot.<br><br>Line 12 calls <b><i>instance_variable_get</i></b> which returns the value of an instance variable or nil (which is the case the first time the method is called) if the instance variable has not been set. The @ part of the variable name should be included for regular instance variables.<br><br>Line 14 checks to see if we got an actual value from <b><i>instance_variable_get</i></b> and if one exists, we return the cached value. If <b>cache</b> is nil, we then enter the else part of the if statement on line 17 where we actually call the real method by using Ruby's <b><i>send</i></b> method which allows you to call methods dynamically. Here we are sending a message to the aliased method we created via <b><i>alias_method</i></b> on line 9. We then use <b>instance_value_set</b> to create an instance variable that will hold the value of the returned method call before returning the result of the call.<br><br>The end result is this:<br><br></span><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 1</font> obj = ClassWithExpensiveMethod.new</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 2</font> </font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 3</font> puts obj.long_method #actual method is called</font></span></p><p style="margin:0px"><span style="font-style:normal"><font size="4"><font color="#2b91af"> 4</font> puts obj.long_method #cached result is returned</font></span></p></div><br><br><span style="font-style:normal">And that is it, nothing to it. Perfectly simple and any bugs happening would be easy to track down, right? We are calling include on the inclusor that in turn calls extend to mixin class methods to the inclusor's eigenclass. That actually makes sense to me, you might need to dig about the web like I did but I think these concepts are worth noting.<br><br>I got through the whole post without ranting uncontrollably about how much I hate Silverlight and Xaml.<br><br><br><br><br><br><br></span><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com5tag:blogger.com,1999:blog-5221465892382713395.post-49871141561939627452010-07-24T06:21:00.001-07:002010-07-24T08:46:50.238-07:00Ditching the Service Layer and IOC for Mxins in Ruby<div style="background-color:white;color:black;font-family:'Courier New'"><font face="Verdana">I still classify myself as very new to Ruby on rails, although I like many other .NET developers are annoying the life out of other people by droning on about how great the brave new(?) world of Ruby is. Before I start, if anyone disagrees with what I am about to post here then please, please, please write a comment below that explains a better approach.<br><br>I have been developing in .NET since framework beta 1 (doesn't everyone say that) since 1892. I like many, (or so it seems) .NET developers are looking at other frameworks and platforms like Ruby on Rails due to a frustration with the continuous abominations coming out of Microsoft like RIA services, oData, Entity Framework, Worflow, Silverlight etc. Microsoft continually wants to lower the bar to lesser developers and all this does is....attract lesser developers who offer nothing back. I find myself just hating everything. I also find myself getting angry at the constant ill appropriated blog posts about Domain, Driven Design and other nonsense like CQRS that fill up my RSS reader from the better educated .NET developers. Am I alone in thinking that DDD is just impractical for 99% of all the applications that it is used in and the blue book was pretty dull? I still enjoy C# but the surrounding patterns, practices and new features just really annoy me now. I cannot work out if I am truly this enamoured with Ruby on Rails or is it that it is just different. I am probably just hankering after something new and shiny and Ruby on Rails ticks all the new and shiny boxes for me. I find the dynamic paradigms fascinating and I have been pouring over the source of things like the Rails ORM <a href="http://ar.rubyonrails.org/" id="mlpn" title="ActiveRecord">ActiveRecord</a> for an idea of how to do things differently. The source of ActiveRecord is crazy, crazy, crazy but I do have a better understanding of just what shape shifting is available in a dynmaic language now. One thing, I really do not want to do is to start coding in a Ruby on Rails application, just like I was coding a .NET application. What would be the point? I want to fully explore what a dynamic language has to offer and why so many Java developers before us felt the need to move here. <br></font><br><font face="Verdana">I am working on a MicroIsv project in my spare time and I came across an interesting decision today which has lead me to this post. My infant product will scrape text from a yellow pages like directory website and then go through a data scrubbing process to make sense of the unstructured data. I could write the data scrubber myself but I would have to start delving into <b>natural language processing</b> or other time consuming concepts that would probably mean postponing the release date of the application to one that stretches beyond my lifetime. Thankfully there are a number of 3rd party webservices, REST webservices or whateverwebservices that can unburden me of this somewhat difficult task. I do not want to favour one just now and I also want to leave the option of changing provider if and when I feel the need. I also do not want to call the service from my unit tests for all the reasons that are usually trotted out for calling external resources with unit tests. The obvious thing to do is to isolate this functionality.<br><br>If I was doing this in .NET, I would create a service layer that is defined by an inteface for the requested service just like below:<br></font><br><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> <font color="#0000ff">public</font> <font color="#0000ff">interface</font> <font color="#2b91af">IDataScrubber</font></font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> {</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> Lead ScrubData(<font color="#2b91af">Dictionary</font><<font color="#0000ff">string</font>, <font color="#0000ff">object</font>> parts);</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> }</font></p></div><br><font face="Verdana">I would then autowire this service into the IOC container of my choice that would inject the service via constructor injection into a controller or something where I would use it. This allows me to mock the service and also depend on an interface rather than the concrete implementation. <br><br>While looking at a lot of the Ruby source code that I have installed in the form of RubyGems on my hard drive, I just do not see this type of implementation. There are no interfaces in Ruby for starters and you rarely see an IOC container although they do exist. This has led me to seek a way that takes advantage of the flexibility of a dynamic languages. As I said previously, I see little point in writing this application in Ruby as if it were a static language like C#. I want to try to comprehend the power and malleability of a dynamic language.<br><br>The first approach does not lean on any dynamic magic, I am just going to suggest using default parameters that are part of Ruby. I can simply suggest a default implementation in the constructor of the Parser class below. Initialize is the constructor of a Ruby object if you did not know.</font><p style="margin:0px"><font size="2"><br></font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> class Parser</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> attr_reader :scrubber</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> def initialize(<b><font size="4">scrubber = DataScrubber.new</font></b>)</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 5</font> @scrubber = scrubber</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 6</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 7</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 8</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 9</font> class DataScrubber</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 10</font> #real implementation goes here</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 11</font> end</font></p></div><br><br>That is ridiculously easy and for simple cases, there is no reason not to use this approach, what could be simpler. In the constructor of Parser, I have a default parameter for the scrubber member variable. This will create the real DataScrubber if none other is offered when creating an instance of Parser. When testing, I can simply create the parser with a stubbed out version of the DataScrubber.<br><br>I still do not feel satisfied by this answer and next I want to look at <a href="http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/" id="m70y" title="mixins">mixins</a>. From what I can tell in Ruby, it seems they use fewer classes than other OO languages like C#. In C# we tend to compose things through a lot of small classes that run under the guise of <b>Separation of Concerns</b>. The problem with this is that there is generally a lot of implementation details needed when creating these interwoven class structures. Hence we have the IOC container to take away this complex creation process that often exists. <br>With Mixins, we can implement behaviour in one module that gets mixed into one or more classes that should have that behaviour. A good way to ensure that the concerns are, in fact separated is to develop the mixins in a test driven manner. <br><br>We could define our DataScrubber in a module like this:<br><br><div style="background-color:white;color:black;font-family:'Courier New'"><div style="background-color:white;color:black;font-family:'Courier New'"><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> module DataScrubber</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> def self.included(base)</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> puts "module included by #{base}"</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 5</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 6</font> def scrub_data(parts = {})</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 7</font> parts.keys.each{|p| puts "doing something with #{parts[p]}"}</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 8</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 9</font> end</font></p><p style="margin:0px"><br><font face="Verdana">Note, I am using the hook method included above that fires whenever it is included by another object. The hook method here is purely for illustration but it is useful to know it is there.</font><br></p></div></div><div><br>This then allows me to test the mixin in isolation like this by mixing it in to a simple empty instance</div><br><div style="background-color:white;color:black;font-family:'Courier New'"><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> class MixinTest < ActiveSupport::TestCase</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> test "should test DataScrubber" do</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> scrubber = Class.new</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 5</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 6</font> scrubber.instance_eval do</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 7</font> include DataScrubber</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 8</font> end</font></p><p style="margin:0px"><font color="#2b91af"> 9</font> </p><p style="margin:0px"><font size="2"><font color="#2b91af"> 10</font> scrubber.scrub_data({:one => "one", :two => "two"})</font></p><p style="margin:0px"><font color="#2b91af"> 11</font> </p><p style="margin:0px"><font size="2"><font color="#2b91af"> 12</font> #assert behaviour here</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 13</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 14</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 15</font> end<br><br><br><font face="Verdana">Here I am creating a blank instance of an object and passing a block or anonymous method to instance_eval which will evaluate a block in the context of the instance. You can add, override and modify object instances at runtime using instance_eval which is part of Ruby's dynamic playground. I like this approach because we are not having to create a plethora of classes to add behaviour and I can test the behaviour in isolation. If you have ever looked into creating Mixins in .NET then you will know how difficult a task this is.</font></font></p></div><br>When it comes to running a unit test against the Parser class that has this functionality mixed in via the <b>include</b> statement, I somehow want to stub out this behaviour when the DataScrubber is mixed into the Parser class like it is below:<br><br><p style="background:white;color:black;font-family:Courier New;margin:0px"></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 1</font> class Parser</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 2</font> include DataScrubber</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 3</font></font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 4</font> def parse()</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 5</font> parts = {:one => "one", :two => "two"}</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 6</font> </font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 7</font> scrub_data(parts)</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 8</font> end</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 9</font> </font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 10</font> def initialize </font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 11</font> end</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"><font size="2"><font color="#2b91af"> 12</font> end</font></p><p style="background:white;color:black;font-family:Courier New;margin:0px"> <br><font face="Verdana">As ruby is a dynamic language, this is very easy, here is one way of doing this:</font><br><br></p><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> test "should stub out DataScrubber" do</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> parser = Parser.new</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> parser.instance_eval do</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 5</font> undef scrub_data #not strictly necessary</font></p><p style="margin:0px"> <font color="#2b91af">6</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 7</font> def scrub_data(parts)</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 8</font> puts "stubbed out method"</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 9</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 10</font> end</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 11</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 12</font> parser.parse</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 13</font> end<br></font></p><p style="margin:0px"> </p></div><div style="background:white;color:black;font-family:Courier New"><font face="Verdana">Here I am using our old friend instance_eval to open up the parser instance and undefine the scrub_data method that has been mixed in before re-adding it with the behaviour that I want to run in my tests without having to call the 3rd party service.<br><br>Of course any modern platform is defined by the number of mocking frameworks on offer and Ruby certainly has plenty, </font><a href="http://flexmock.rubyforge.org/" id="ihzr" title="flexmock"><font face="Verdana">flexmock</font></a><font face="Verdana"> is my weapon of choice and below is an example of how I could use flexmock to create a partial mock to return some test data:</font></div><div style="background:white;color:black;font-family:Courier New"><br> </div><div style="background:white;color:black;font-family:Courier New"><p style="margin:0px"><font size="2"><font color="#2b91af"> 1</font> test "should stub out DataScrubber" do</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 2</font> parser = Parser.new</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 3</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 4</font> flexmock(parser).should_receive(:scrub_data).and_return { "some data"}</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 5</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 6</font> result = parser.parse</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 7</font> </font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 8</font> #assert result data</font></p><p style="margin:0px"><font size="2"><font color="#2b91af"> 9</font> end</font></p></div><p style="background:white;color:black;font-family:Courier New"> <br><font face="Verdana">The last approach I am going to mention is inheriting from </font><a href="http://www.therailsway.com/2007/9/3/using-activeresource-to-consume-web-services"><font face="Verdana">ActiveResource</font></a><font face="Verdana">. I am not sure this is appropriate to me as the service must understand Rails-style URLs and I might not have that luxury with whatever 3rd party service provider, I choose.<br><br></font></p><div style="background:white;color:black;font-family:Courier New"><font face="Verdana">I think mixins are the right choice for me, the Ruby way seems to be (and I could be wrong, I regularly am) to create lots of methods as opposed to interwoven class structures that require complex object creation techniques, just to get to the functionality you require.<br><br>If I was approaching this problem in C#, I would say, "I would isolate this functionality in a service that I inject into the controller" but in Ruby, I would say "I would write this as a Ruby module that I include in my class".<br><br>Testing, stubbing or mocking are, as is often stated, ridiculously easy.</font></div><p><br><font face="Verdana">It is funny to note that when I was musing about this earlier on twitter somebody who describes himself in his bio as a "passionate Rubyist" made the following questionable comment "<b>Composing services with mixins? Ouch, enjoy the impending doom</b>.". I suspect this guy is like me, a .NET developer who now suddenly thinks he is a "rubyist". </font><font face="Verdana">Another thought is that perhaps real Ruby on Rails guys are thinking of moving onto Clojure or Scala, now that Ruby is no longer as hip as it was.<br></font><br></p><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com11tag:blogger.com,1999:blog-5221465892382713395.post-85809691607595077992010-06-28T00:42:00.001-07:002010-06-28T02:21:04.284-07:00Reasons to hate Silverlight Part I<p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="wc0c" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_460d6hdd6c3_b" style="height:349px;width:457px"></div>Looking back now, I nearly fell for it, I feel so stupid but I am glad that my natural underlying cynicism of all things new and shinny let me look at this emerging Microsoft technology with an impartial mind. The marketing that came out of Microsoft was truly outlandish. I do of course talk about Silverlight which is the latest evil incarnation of that festering sore on the landscape of web development known as the browser plugin. I have hated flash from an end user point of view for a long time, this was further intensified when I worked for a web agency which meant lots of interaction with this island of proprietary insanity that sits menacingly in prominent areas of public facing websites. <p></p><br><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">Looking back now, the hysteria and hype that greeted the build up and subsequent launch of silverlight was completely over the top. It was flaunted as a game changer which was the future of the web. Funny how they flaunted a flash clone as original and ground breaking but originality is not something Microsoft is renowned for. JavaScript and CSS were nothing more than short term hacks that were keeping the ubiquitous seat warm until Silverlight was ready for market. I debated this on the once popular ALT.NET forum in this </font><a href="http://tech.groups.yahoo.com/group/altdotnet/message/16589" id="ifl1" title="thread"><font face="Arial">thread</font></a><font face="Arial"> some time ago, were I was told by developers that this was the future. The most hilarious argument I got was that "I would be left be behind", if I did not open my arms and embrace this brave new world with the warmth and affection that is best compared to a mother and a new borne baby. Before getting into the technical details of why this "brave new world", is just a rehash of the last "brave new world" promoted by Microsoft, a.k.a. Webforms, let me spell it out to you the reader, who and what Microsoft are aiming this "ugly girl at the dance" at. </font></p><br><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">Webforms is now grossly out of favour with the intelligencia of .NET that were formerly the artist known as ALT.NET. These cool kids have moved onto ASP.NET MVC which follows a more traditional and honest web development model. It is now a commonly agreed perception that Webforms is a leaky abstraction directed totally at VB6 developers who could not leave the paradigm of statefull VB6 forms behind them. </font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">Let me illustrate this with a vignette from my employment history…….</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">I worked as a consultant for a leading educational software provider in London (I am so tempted to mention them by name) were they had an army of old VB6 developers gleefully and eloquently creating heinous crimes in the field of web development that are analogous to Hitler's holocaust. Their ignorant approach was to use datasets bound to datagrids throughout the application, not only was the average page size gargantuan with ViewState multiplying exponentially with every postback but they stuffed everything in the Session which was stored in SQL Server. It was truly unbelievable that something so ugly was being used by the leading educational organisations of the UK. This is a fairly common web forms shop were square pegs are hammered into round holes who develop time bomb websites that are primed to explode in incendiary expansions of cash. The very fact that they had to hire consultants like myself to unscramble the mess is testimony that this just did not work. They got the website up and working quickly and then spent a lot of time and money rewriting it to perform like a high traffic website should.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">The type of developer webforms is aimed at is the mission statement of the vast majority of Microsoft products, it is of course the drag and drop world of development. You simply choose what you want from the designer and press F5, hey presto, you have yourself a piece of demoware software that is held together with double side sticky tape and string and has a shelf life of two days. Microsoft loves this space, even Scott Hanselman has joined in with </font><a href="http://www.hanselman.com/blog/CreatingAnODataAPIForStackOverflowIncludingXMLAndJSONIn30Minutes.aspx" id="fk8u" title="this"><font face="Arial">this</font></a><font face="Arial"> more measured 30 minute application. This is the developer who Microsoft are aiming this at. This is the market that EF is marketed at. They love this shit. They like impractical apps in 30 milliseconds for shows like MIX.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"></p><p style="font-family:helvetica;font-style:normal;margin:0px"></p><div id="ui6i" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_461ct72k7cj_b" style="height:294px;width:250px"></div><br>In development, we often talk about personas, a persona is a mythical user of the system who has the traits of the type of user the software is aimed at. Let us create the persona of a Silverlight developer, we will call him Bob. Bob is a lazy fuckwit. Bob has no real interest in development or doing things right, Bob just wants to cut and paste code from Google whenever he is stuck. Bob does not look up from the developer world that Microsoft has painted for him, he would never dream of looking at another web framework in order to foster some new ideas. Bob hates JavaScript and CSS, he has absolutely no interest in learning them. Bob loves other languages, as long as they are C#. Poor hapless Bob could not even compile his code if you took Visual Studio away from him, he only ever uses the command line to issue the iisreset command. Bob will never develop anything that cannot be developed in Visual Studio. Bob likes the designer surfaces of Visual Studio. Bob sees Silvelight as a shortcut to browser development which means he can stay totally in the world of C# and not have to learn horrible JavaScript. Bob has heard of the jaw dropingly cool JavaScript framework called JQuery, but Bob has no idea how something so popular could be written in anything other than C#. Bob will stay in his blinkered hole, unaware of the outside world that truly is changing to a brave new world. Bob is exactly who Microsoft want in their development army. Microsoft do not want you to develop in anything other than Visual Studio or perhaps Expression blend. They do not want you to learn the ubiquitous browser display language of HTML, they do not even really want you to learn XAML, they want you hooked on your inability to do anything outside of a designer. Are you a Bob? Do you ever lift your head above the one world view of visual studio? I will move onto XAML in a future post but I want to put forth the conspiracy theory that Microsoft prospers from your own inadequacies. They want you to have a reliance on Visual Studio in the same way as a super fly pimp guy wants his ho's dependant on heroin to gain their dependence. <p></p><p style="font-family:helvetica;font-style:normal;margin:0px"></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">Another conspiracy theory I have is that Microsoft has got cold feet over IronRuby as it might lead to a further brain drain of the developers formerly known as ALT.NET. More and more of these guys have had enough after tasting the crack cocaine of Ruby on Rails that gives the addict, the feeling of how efficient a web framework can be. The IronRuby story is getting picked up by some but not many. I do not expect to hear Microsoft making any big noises about IronRuby on rails anytime soon. The majority of the posts I have seen, show how to use IronRuby used with WPF!! You could not make it up. I found </font><a href="http://blog.glenc.net/2008/07/25/ironruby-a-sharepoint-developers-best-friend/" id="tfuw" title="this"><font face="Arial">this</font></a><font face="Arial"> example which passes for great ironic humour were the author sets out to show how IronRuby can be used with sharepoint. Ironic IronRuby on rails is a more fitting title for this total farce that just goes to show how Microsoft or indeed its developers do not get Ruby on Rails and are instead shoe horning IronRuby into impractical and ridiculous places.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">In writing this post, I could have very easily just reworded Steve Jobs </font><a href="http://www.apple.com/hotnews/thoughts-on-flash/" id="fzov" title="post"><font face="Arial">post</font></a><font face="Arial"> about flash and do a search and replace on the word flash and replace it with the word Silverlight. The arguments against Silverlight are just as valid as those against flash. I will simply </font><a href="http://www.apple.com/hotnews/thoughts-on-flash/" id="ddld" title="link"><font face="Arial">link</font></a><font face="Arial"> to the post and let you judge for yourself. Instead of rewording that post, I wanted to take a different slant on why you should hate silverlight. I want to paint a warning to anybody actively indulging in silverlight development that you could be making a serious career wrong turn. The world is changing and HTML 5 is the actual brave new world that Silverlight claimed to be. By staying in Silverlight, you will be learning skills that cannot be transferred to any other platform. If you know HTML and JavaScript, you can easily take these skills with you from platform to platform. Spending every waking hour knee deep in Visual Studio and only ever developing in C# will make you a shallow uneducated developer. </font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">I think it is worth noting that Microsoft is getting wise to HTML 5 and are now fully embracing it in IE9 with some demoware </font><a href="http://mashable.com/2010/06/25/ie9-chrome-firefox/" id="xbpn" title="here"><font face="Arial">here</font></a><font face="Arial">. Do not expect Microsoft to do anything less than drop Silverlight like a stone if HTML 5 is popular on IE9.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">There are enough posts written about the joys of HTML 5 for me not to bother mentioning its freshness and newness. Yes, it is turning into the catchall for anything web related, just like web 2.0 did and apple's HTML 5 is in someways just as proprietary as silverlight but this is the future of the web. JavaScript and CSS have had a resurrection, not the flaunted death. The talent of the JQuery team are testimony to who are now developing JavaScript. Are these similar types of developer drawn to Silverlight like a moth to a flame? Are the likes of </font><a href="http://workingwithrails.com/person/871-jim-weirich" id="drhb" title="Jim weirich"><font face="Arial">Jim weirich</font></a><font face="Arial"> singing the praises about RIA and oData? I think you will find nobody of any note is drawn to this platform in the same way as tired Java developers fled to Ruby on Rails. oData is another tenant of the strangely name RIA platform. What oDATA has done to the ATOM specification is nothing short of rape and yet another reason to be truly repulsed by RIA services. </font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">We no longer need these ugly plugins that do not play by natural browser rules, alienating themselves like inner city ghettos in no go areas of the web page. We as developers need skills that are transferable. There is a world outside of Visual Studio that can only progress you as a developer. Or are you a Bob, blinkered, ignorant and tied, like a dependant crack addict to the rocks of Visual Studio. Don't be a follower, don't be a sheep and learn to think for yourself. The pragmatic programmer book stated that you should learn a new language a year, how about learning a language outside of Visual Studio? If this scares you then you need to seriously do some soul searching or maybe go and work with Bob.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><br></p><p style="font-family:helvetica;font-style:normal;margin:0px"><font face="Arial">Next I am going to rant about the truly, ugly mess that is XAML. XAML is an xml grammar that cannot even be defined in an XSD schema because it is so ugly. XAML is like XHTML only it can crash an IDE.</font></p><p style="font-family:helvetica;font-style:normal;margin:0px"><a href="http://twitter.com/jimweirich" id="lj5b" title="Jim Weirec"></a></p><p></p><br><p></p><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com12tag:blogger.com,1999:blog-5221465892382713395.post-88978153326273130742010-04-09T09:49:00.001-07:002010-04-09T09:53:18.951-07:00Invention is not invented hereIn keeping with the subject matter, I want to rip off my own <a href="http://thesoftwaresimpleton.blogspot.com/2010/01/go-fubumvc-yourself.html" id="sj7t" title="Go FUBUMVC yourself">Go FUBUMVC yourself</a> post by rehashing the idea and writing about it again.<br><br>It is a blatant rip off of the last post. It is lazy and requires little thought. I could write about something new but why bother when this is guaranteed to get some traction.<br><br><div>Now on with the show.........<br><br>Not a week goes by without my RSS reader mentioning a new .NET OSS project that is a complete rip off of an existing project.<br><br>The latest one to sadden my eye is the <a href="http://codeofrob.com/archive/2010/04/06/autopoco-v0.1-released.aspx" id="h_82" title="AutoPoco">AutoPoco</a> project by Rob Ashton. We already have <a href="http://nbuilder.org/" id="j-sz" title="NBuilder">NBuilder</a> and <a href="http://autofixture.codeplex.com/" id="z3h9" title="AutoFixture">AutoFixture</a> so what on earth do we need another one of these for? <br><br>It then turns out that Karl Seguin or whatever his name is states that he wants to write another validation framework. Are you absolutely mad? Another one? We already have 3,089,298 validation frameworks muddying the waters and taking the word diversity to new levels. What do you really hope to achieve by writing another validation framework? Maybe we can really abuse lamdas into a totally ridiculous syntax. Why not put a fluent interface on top of a fluent interface.<br><br>I think what saddens me the most is how many man hours are required for the new project to get into a state of parity with the existing project that is being ripped off. <br><br>We will never compete with platforms like Ruby on Rails without invention or innovation.<br><br>There are of course some less time consuming acts that you can choose rather than a total rewrite of an existing system:<br><br><ol><li>Get involved with the existing project you are about to rip off. This will never happen as the developer wants his name on the door.</li><li>Fork the code of the existing project and save yourself the hassle of having to write all this crap from the start.</li><li>Write another IoC container. </li></ol><br><div>I am further saddened by the fact that the developers in question are probably quite talented and could put this time to the betterment of the .NET ecosystem.<br><br>My theories as to the <b>why </b>of all this blatant plagiarism are this:<br><br><b><i>it is time consuming to find a worthwhile problem to solve</i></b>. <br><br>This time could be spent writing code. Finding a good problem means placing your brain in unfamiliar places. I know myself how difficult this is after spending two months coming up with a MicroIsv product idea to pursue. My urge to go down the familiar routes of project management and CRM was strong but I stuck with it and eventually came up with a product I was able to in some ways convince myself has potential and has a market with some good and testable market research. <br><br></div><div><b><i>I just want to write code</i></b></div><br>As developers we want to write code, we do not want to create mindmaps or brainstorm through a list of possibilities before going through the whole process again to define the problem space. I could easily pop out a mocking framework or a .NET build engine in the same time with less effort.<br><br><div><b><i>I might fail<br></i></b><br>I know about failure on this front more than most as in a lot of ways <a href="http://www.hornget.net/packages/" id="p1o6" title="horn">horn</a> has been a failure. It is certainly a failure in the community I tried to build around it and it still has some failings in the technical solution. I can though say I tried. I can also say that my mind was placed in some very unfamiliar and unusual places as I thought about the problem. With a pre-defined problem space like AutoPoco is ripping off, what do I learn on a non-coding basis. Free your mind and your ass will follow.<br><br>Why not pick an existing problem and rewrite it? I can change it slightly to my needs and if all else fails, I can always look at the source of the existing problem.</div><br>I will now court ultimate controversy and hatred from the .NET community by stating that one of its favourite son's is the ultimate plagiarist.<br><br>I speak of course of <a href="http://ayende.com/blog/default.aspx" id="sh0v" title="Ayende">Ayende</a>, before I list his list of similar projects, I do want to say that the guy has done amazing things for the community and at one point I looked forward to his blog posts with glee. He has done more than most which is why I find it frustrating that he has not used his talent for more innovative ends.<br><br>Did we need another MSMQ with Rhino Queues?<br>Do we need another document database?<br>Do we need another memcached clone?<br>etc., etc.</div><br>The answer to all the above is no and I would say his most innovative project to date is NHProf which is ironically commercial. <br><br>I expect any Ayende sycophants reading this who take crawling to their master to new and sickening levels to proclaim a jihad against me.<br><br>Come and have a go if you think you are hard enough!!!!<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com18tag:blogger.com,1999:blog-5221465892382713395.post-15787253713132937162010-04-08T00:48:00.001-07:002010-05-12T06:45:52.383-07:00Rails - Day 17 - Update and Delete<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II"></a><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" id="w.73" title="post">post</a>, we covered off validation.<br><br>We have been using a mock application of an expense tracker to illustrate the key points. The expense records the expenses incurred running a small business.<br><br>In this post we want to finish the application off by completing our pseudo application by allowing updates and deletes of existing records.<br><br>Below is the index page of our expenses application that will allow a user to edit and delete existing expenses that we can create with our code thus far:<br><br><div id="p8sp" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_436d4qm44xd_b" style="height:142px;width:648px"></div><br>The edit action is analogous to the new action in that it just transfers control to a web page that will allow us to alter the details of an existing <b>Expense</b> model object.<br><br>As we mentioned in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="o:au" title="post">post</a>, the edit url takes the form of <b>/expenses/123/edit</b>.<br><br>Below is the haml created for each row in the above table:<br><br><div id="kze2" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_437nvxvvtfs_b" style="height:49px;width:648px"><br><br></div>This generates the following url:<br><br><div id="r3gl" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_438fzwg82gr_b" style="height:50px;width:439px"></div><br>At the moment, the haml that renders the screen below is all contained in the <b>new.html.haml:<br><br><img height="388" src="http://docs.google.com/File?id=dgjpt2xx_400ffhp8nf5_b" width="754"></b><br><br>We want to abstract the <b>Expense</b> model rendering from the <b>new.html.haml</b> in order to reuse it from the <b>edit.html.haml </b>which is the template the rails runtime will look for by convention whenever a url similar to the one below is called:<br><br><b>/expenses/124/edit<br><br></b>The rails runtime will predictably look for a template named <b>edit.html.haml</b> by convention.<br><br>We extract the formbuilder haml and place it in a partial named <b>_form.html.haml </b>and update the <b>new.html.haml </b>to render from the partial:<br><br><div id="zaye" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_439hk3t7dff_b" style="height:200px;width:634px"></div>The <b>:locals => {:f => f}</b> expression passes the formbuilder instance to the partial.<br><br>We can now easily create an <b>edit.html.haml</b> that will incorporate the partial:<br><br><div id="jrcf" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_440cxb6qjdr_b" style="height:205px;width:620px"><br>Next we want to create an <b>edit</b> action in our <b>ExpenseController</b> that will retrieve the <b>@expense</b> instance from the database:<br><div id="ja1q" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_441f8g9x3cb_b" style="height:120px;width:485px"><br><br>The<b> @expense_types </b>instance variable will contain a collection of all the <b>ExpenseType </b>objects that are mapped from records in the <b>expense_types</b> table. <br></div><br>With these sparse lines of code in place, we can now display an expense model that is ready for editing when we click on the edit link of one of the expense records that are displayed on the index page.<br><br><div id="qtmw" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_442gb4hkztd_b" style="height:301px;width:502px"></div>Rails routes several restful actions onto every resource that is generated by adding an entry similar to, <b>map.resources :expenses </b>to the routes.rb file.<br><br>The <b>update</b> action is the correct and obvious choice for updating our model.<br><br>The intricacies of setting up the relationships between the objects was done in the last post when we used <b>accepts_nested_attributes_for </b>to enable the complex Expense object to be created from the form fields submitted from the above page.<br><br>Below is the simple <b>update</b> action in our <b>ExpenseController </b>that will take care of the update or redirect to the edit and <br><br><div id="st1w" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_444f3rpnfg5_b" style="height:242px;width:648px"><br><br>In the above action, we are loading the expense object into memory and then calling the <b>update_attributes</b> method which updates all the attributes of a model from the passed in hash. If the object is invalid, the saving fill fail and false will be returned.<br><br>In the event of a successful call to <b>update_attributes</b>, the user will be redirected to the index page where a confirmation message will be displayed which has been placed in the <b>flash provider</b>.<br><br><div id="u6hx" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_445ckw4krfs_b" style="height:165px;width:366px"><br><h1>Deleting a Model</h1>This is probably the simplest action to complete. The delete link takes the form of the bin icon in the screenshot below.<br><br><div id="qswp" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_436d4qm44xd_b" style="height:142px;width:648px"></div><br></div>This link is rendered from the following HAML:<br><br><b>=link_to "", {:action => "destroy", :id => e.id}, :method => :delete, :class => "icon-delete", :confirm => "Are you sure"</b><br><br>This will render an anchor tag that links to a controller action called <b>destroy </b>and passing in the id of the expense we want to delete. You can also see that the javascript confirm mechanism can be hooked up with the <b>:confirm => "Are you sure"</b> expression.<br><br>Below is the HTML that is rendered from the HAML:<br><br><a href="<a href="http://docs.google.com/expenses/17">/expenses/17</a>" class="icon-delete" onclick="if (confirm('Are you sure')) { var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method'); m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input'); s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value', 'rW75EzBpl9r103D8zdIzX18upwh1Vs5dyp9RWYhJ31E='); f.appendChild(s);f.submit(); };return false;"></a><br><br>The above link generates a lot of javascript which constructs a form and posts it to the server that will in turn be routed to the <b>destroy</b> action of the <b>ExpensesController</b>. <br><br>This will call the following action in the <b>ExpensesController</b>:<br><br><div id="jim7" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_446drghrwcq_b" style="height:221px;width:648px"></div> We simply retrieve the expense instance and call the destroy method. We then place the confirmation message in the flash provider and redirect to the index page:<br><br><div id="se5." style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_447dfgzzs6f_b" style="height:116px;width:226px"></div>This ends my introduction to rails series. <br><br>You can download the code that I created writing these posts from github <a href="http://github.com/dagda1/expensetracker" id="m8.e" title="here">here</a>.<br><br><br><br><br></div></div><br><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-85072483421226789502010-04-06T00:19:00.001-07:002010-05-12T06:45:29.306-07:00Rails - Day 16 - Validation<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" id="qjpk" title="post">post</a>, we took a deeper look into working with a complex object and the Rails form builder.<br><br>We have been using a mock application example of an expense tracker to illustrate the key points. The expense tracker will record the expenses incurred running a small business.<br><br>The complex object in question is an <b>Expense </b>model object that is made up of the following structure:<br><br><div id="zoqs" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_422d6jzvgfg_b" style="height:160px;width:648px"></div><br>Below is the form we have constructed to capture an expense structure that is created from the inputs submitted in this form:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_400ffhp8nf5_b"><br><br>One thing that has not been mentioned so far is how to validate the user supplied input that is sent from this form to the server. In this post we want to ensure that:<br><br><ul><li>The External reference input has a value. </li><li>The posting Description input has a value </li><li>The user has selected an Expense Type from the dropdown </li><li>The net field has a valid monetary value added. The net field is an attribute of the Expense's child object ExpensePayment.</li></ul><br>If we take our first requirement from the above, we can create the following <b>unit </b>test to ensure our assertions:<br><br><div id="tzd7" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_426s2t6k7dw_b" style="height:322px;width:648px"></div><br>Notice that there is also a test to check that the expense object is valid. It is important to start from a good position and then assert failures.<br><br><div>In the above test, we are creating a valid <b>@expense object</b> and then setting the field we want to test (<b>external_reference</b>) to <b>nil</b>.<br><br>This test obviously fails as no validation instructions have been added on the Expense model. <br><br><h1>Rails Model Validators</h1>When developing a Ruby on Rails application it is a good idea to use ActiveRecord validators to ensure the state of a model before trying a persist it to the database. This type of validation is also known as first pass validation.<br><br>The most common approach is to use the declarative <b>validates_xxxx_of</b> class methods.<br><br>In order to make the above test pass, we use the <b>validates_presence_of</b> ActiveRecord class method to ensure an expense has an <b>expense_payment </b>attribute:<br><br><div id="l5gp" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_424d6tgwqcb_b" style="height:173px;width:648px"></div><br>Our test now passes. The <b>validates_presence_of </b>method takes a list of attribute names that we want to ensure have non nil values. <br><br>Other similar attributes are:<br><br></div><ul><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002173">create!</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002163">validates_acceptance_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002171">validates_associated</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002162">validates_confirmation_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002161">validates_each</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002170">validates_exclusion_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002168">validates_format_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002169">validates_inclusion_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002165">validates_length_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002172">validates_numericality_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002164">validates_presence_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002166">validates_size_of</a></li><li><a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M002167">validates_uniqueness_of</a></li></ul><br>We can validate the other attributes that we want to ensure are required like this:<br><br><b>validates_presence_of :external_reference, :description, :expense_date<br></b><br>But what about associated objects? How can we verify that the user has selected an ExpenseType from the dropdown. In our model, an <b>Expense</b> belongs to an <b>ExpenseType</b>.<br><br>In order to test this, we create the following test:<br><br><div id="rsr1" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_427f3xjqmf3_b" style="height:128px;width:648px"></div><br><br>This type of relationship is very easy to validate.<br><br>The above test obviously fails but if we update the model to add the <b>expense_type_id </b>to the list of symbols passed as arguments to the <b>validates_presence_of </b>then all is good.<br><br><div>Our final two requirements concern the <b>has_one ExpensePayment</b> relationship. There are two things we need to test:<br><br><ul><li>We need to ensure that there is an <b>ExpensePayment </b>child object contained within the Expense object. </li><li>We need to ensure that the contained child <b>ExpensePayment </b> object has a non nil name field.</li></ul><br>As ever we create the following failing tests:<br><br><div id="d6kz" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_428f8rbv9cj_b" style="height:239px;width:648px"></div><br>In order to make the first test pass which ensures that an <b>expense_payment </b>object is contained with in the Expense model, we just update the symbol list <b>validates_presence_of </b>to include the <b>:expense_payment</b> attribute<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_430g2mmqkfp_b"><br>This will enable our first assertion to pass but the second assertion still fails.<br><br>The first step is to add a call to <b>validates_presence_of</b> class method in the <b>ExpensePayment</b> object and include the <b>net</b> attribute.<br><br><div id="a8jd" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_431dktxz6gd_b" style="height:115px;width:502px"></div>We then update the Expense object to include a call to the ActiveRecord validation class method <b>validates_associated.<br></b><br><div id="t3q5" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_432dtvp2xc3_b" style="height:289px;width:480px"></div><div id="rhvr" style="text-align:left"><br>Above we can see the call to <b>validates_associated :expense_payment</b> which will instruct the framework to include the child object validations of the ExpensePayment model in the <b>@expense.errors</b> array.</div><br>With our validation tested, we now need to add the following to our <b>new.html.haml</b> view that will take care of displaying any validation errors:<br><div id="uvqp" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_433gnw9ztfd_b" style="height:104px;width:365px"></div><br>And that is it, the <b>error_messages_for :expense</b> expression will display any error messages contained within the <b>@expenses.errors</b> array.<br><br>Below is how these error messages are displayed on the page. Obviously you would style the div containing the error messages but as this is post 16, I simply cannot be bothered:<br><br><div id="b5uz" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_435drrzsgfb_b" style="height:93px;width:324px"></div><br></div><div>Obviously, this type of validation is very simplistic. There are many hooks to get into this validation process for more complex scenarios. There are callbacks available on <b>:before_save</b> or <b>:after_save</b> or you can even override the <b>validate_on_create </b>or <b>validate_on_update </b>class methods.</div><br>In this post, we have discussed validation at a very high level.<br><br>In the next post, we will finish off the series by completing the update and delete actions.<br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-23278914447101495852010-03-31T23:43:00.001-07:002010-05-12T06:44:26.099-07:00Rails - Day 15 - Rails Forms Part II<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" id="m21d" title="post">post</a> we introduced the rails form builder and showed how easy it is to render a simple form using a simple model object.<br><br>I want to go into much more detail with a pseudo real example because most of the posts about rails forms and ActiveRecord that I have come across illustrate exactly what I did in the last post and show how to use the form builder and a simple model object. Nearly all these examples show a simple model with a one to one mapping on a single table. This is not very real world.<br><br>In this post I want to show how to use a form builder with a slightly more complex model.<br><br>In the past few posts, we have been building up a simple application that will record the expenses a small business owner might incur. <br><br>Below is the view that is rendered from the new action of the <b>ExpensesControlller </b>where a user will add a new expense:<br><br><div id="f5k_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_400ffhp8nf5_b" style="height:333px;width:648px"><br><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" id="oydl" title="post">post</a> we were able to create an <b>Expense</b> object that captured the <b>Paid by Director</b>,<b> Expense date, External reference </b>and <b>Posting Description </b>attributes.<br><br>We covered ActiveRecord relationships in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" id="f98g" title="post">post</a> and defined the relationships between the expense object and its child objects.<br><br>The <b>Expense</b> model is a complex object with two child objects defined by the the relationships below. <br><br><div id="ym:6" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_401gvk56jdg_b" style="height:110px;width:411px"></div><br></div><div id="g4l0" style="text-align:left">We are now going to use the form builder to both set the and retrieve attributes of the child objects through an HTML form.</div><div id="qo-n" style="text-align:left"><br></div>Our first step is to fill the Expense Type dropdown that you can see in the screenshot at the start of the post with any records that exist in the <b>expense_types</b> table.<br><br>The form builder contains a <b>select </b>method which works well for <b>belongs_to </b>relationships. The first argument of the select method is the foreign key of the <b>belongs_to </b>relationship which in our case is <b>expense_type_id</b>. <br><br>We could create a select method similar to the following which has the ActiveRecord call <b>ExpenseType.all</b> to the database defined in the view and I have seen this done quite a lot:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_4026rnzx2dd_b"><br><br><div>We could do that but this feels dirty and ugly. Such a call to the database has no place in our view and instead we add the following code to the new action of the ExpensesController:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_403crrjmccj_b"><br></div><div>The collect method of the array object will iterate over the array and provide a block that allows us to return a new array with different elements. I think I am right in saying that the map and collect methods are the same. A new array is created for each element in the original array that contains the id and name needed to render the option elements of the dropdown.<br><br>We then update our select method to use the instance variable <b>@expense_types</b> instead of the ActiveRecord call:<br><br><div id="xzsv" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_404dpg4zgf2_b" style="height:96px;width:648px"></div><br>This makes me feel much better. The <b>ExpenseType.all</b> call is now in the controller which of course makes it a lot more testable and tidy than adding it directly to the form builder select method. We can also mock the call t<b>o ExpenseType.all</b> which I prefer for testing purposes over direct database calls.<br><br>The select method will take care of the <b>Expense belongs_to ExpenseType </b>relationship. <br><br>There is also a <b>has_one </b>relationship that defines the association between <b>Expense</b> and <b>ExpensePayment </b>as we discussed in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" id="q8:r" title="post">post</a>. <br><br>The ExpensePayment model object will take care of this part of the UI:</div><br><div id="p29t" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_405fw5q53dg_b" style="height:49px;width:343px"></div><br>We also stated in this post that the <b>VAT</b> and <b>Gross</b> attributes are actually derived fields from the <b>Net </b>attribute.<br><br>The calculation for <b>VAT </b>is ((Net / 100) * 17.5) <div>The calculation for <b>Gross </b>is (Net + VAT).</div><br><div>So the only attribute we are concerned with recording from the user is the Net attribute. We should use JQuery and Ajax to update the derived fields Vat and Gross as the user inputs their values.<br><h1>Nested Attributes</h1>Nested attributes allow you to save attributes on associated records through the parent which is exactly the behaviour we want for our <b>Expense has_one ExpensePayment</b> relationship. By default nested attribute updating is turned off but you can enable it using the <b>accepts_nested_attributes_for</b> class method. When you enable nested attributes an attribute writer (property set in .NET speak) is defined on the model. The attribute writer is named after the association, which means that in our example, a new method will be dynamically added to the <b>Expense</b> model:<b>expense_expense_payment=<br></b><br>Enabling nested attributes on a one to one association allows us to create both the <b>Expense </b>and <b>Expense_payment </b>in one go from a params hash that would be passed in from a rails form builder generated form.<br><br>In order to test this, we create the following failing test:<br><br><div id="t35x" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_406fzbzmzfz_b" style="height:368px;width:648px"></div><br>You can see from the above that the hash contains a child hash named <b>:expense_payment_attributes</b> that will represent the child <b>ExpensePayment</b> model object. To make the above test pass, we update the Expense model to the following:<br><br><div id="m0_j" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_408f7b2tgft_b" style="height:157px;width:648px"></div><br>The changes have incorporated the following:<br><br><ol><li>To make the <b>expense_payment</b> attributes available through nested forms we need to add the <b>:expense_payment_attributes </b>convention to the <b>attr_accessible</b> method. We mentioned <b>attr_accessible </b>in the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" id="y.q6" title="post">pos t</a>and this method restricts what methods can be accessed via a web form. This is often overlooked and is a potential security hole. Shame on you if your models do not include this.</li><li>We define the relationship as nested by using the <b>accepts_nested_attributes_for</b> method. </li><li>We have also updated the <b>has_one </b>statement to <b>has_one :expense_payment, :dependent => :destroy</b> which tells ActiveRecord to cascade the delete of the child object when the Expense is deleted.</li></ol><h1>Nested Model Form</h1>In the view that will render the <b>new </b>action, we simply use the <b>fields_for</b> method to expose the fields of the nested model:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_418fmw3km8c_b"><br><br></div><div>It is worth mentioning that we are using the <b>text_field_tag</b> method instead of the <b>text_field</b> method for the readonly attributes <b>vat</b> and <b>gross</b>.</div><br>This will generate the following HTML:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_414dc7w97hm_b"><br><br>You can see that the <b>p.text_field :net </b>expression has generated an input text element with a name attribute of <b>expense[expense_payment][net] </b>which models the <b>Expense has_one ExpenseType</b> relationship and is what rails uses to build the params hash of the relationship.<br><br>If we inspect the params hash that is passed to the <b>create</b> action of the <b>ExpenseController </b>by adding the code <b>raise params.to_yaml</b> to the action, we get the following output:<br><br><div id="g.d9" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_419f93hd5ht_b" style="height:322px;width:463px"></div><br>You can see that the <b>expense_payment_attributes</b> hash is nested within the parent <b>expense</b> hash. This means that we can create an expense object from the form fields in our create action:<br><br><div id="ze0_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_417fscmd7ff_b" style="height:254px;width:648px"></div>We are going to mention validation in the next post but if we now fill out the required fields and submit the form, the <b>create</b> action completes successfully.<br><br>In order to check that the <b>ExpensePayment </b>has made it into the database, we can spark up the rails console with the command <b>ruby script/console</b> and enter the expression <b>ExpressionPayment.all</b>.<br><br><div id="oac8" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_420f7xr8hf3_b" style="height:64px;width:648px"></div><div id="a_f9" style="text-align:left"><br></div><div id="bjv_" style="text-align:left">We can see from the above that our relationship is successfully created.</div><br>I wanted to write this post because in my opinion, a lot of the posts on rails forms do not go into this level of detail for defining relationships.<br><br>I personally do not like having to add the <b>accepts_nested_attributes_for </b>method and would like the framework pick this up automatically. If anyone who knows rails better than me can suggest a better way than this or maybe argue that this way is a good way then please leave a comment.<br><br>In the next post, I am going to touch on how to validate your rails model.<br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-30005187440146392212010-03-31T00:34:00.001-07:002010-05-12T06:43:57.911-07:00Rails - Day 14 - Rails Forms Part I<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" id="zdue" title="post">post</a> we finished off our sparse introduction to Rails ActiveRecord. <br><br>We have now created the UI shell of our example application and we also have tested and defined our model that reflects the job in hand. We re now ready to complete the server side code to tie the two together.<br><br>At the time I was writing this post, ASP.NET MVC 2.0 has just arrived and it is fairly obvious that they have taken a lot of their <i><b>new </b><span style="font-style:normal">ideas for their form builders from the rails form builders.</span><span style="font-style:normal"> </span><br><br><span style="font-style:normal">We have already defined mark up for how the page will look</span><br><br></i><img src="http://docs.google.com/File?id=dgjpt2xx_3032h5k8kg4_b"><br><i><br><span style="font-style:normal">At the moment our <b>new.html.haml</b> page looks like this:<br></span><br></i><div id="w99y" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_352fdjjttfj_b" style="height:733px;width:648px"></div><br><div><i><span style="font-style:normal">We have a simple table contained in a div.</span></i></div><h1>Form Helpers</h1><i><span style="font-style:normal">We are now going to refactor our page to take advantage of the Form helpers that come as part of the Rails library. Form helpers are designed to make working with models easier compared to using standard HTML elements by providing a set of methods for creating forms based on your models. <span style="font-style:normal">The core method of this helper, <b>form_for</b>, gives you the ability to create a form for a model instance.</span> This helper generates the HTML for forms, providing a method for each type of input (e.g. text, password, select etc.). When the form is submitted, the form inputs will be bundled into the <b>params</b> object and passed back to the controller.<br><br></span><span style="font-style:normal">In our example, we are going to create a form for the expense model object. First we will only work on saving the Expense object and then move onto saving the Expense's child objects ExpenseType and ExpensePayment objects. To take advantage of the rails form helpers, we refactor the above haml to the following:</span><br></i><br><div id="zy69" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_393dj979ggg_b" style="height:496px;width:648px"></div><div><br><br>The <b>form_for</b> method is possibly the most popular helper used when generating a form that binds to one type of object. The parameter you pass through fomfor <b>f </b>is of type FormBuilder which offers the other methods you can see like <b>check_box</b>, <b>date_select</b>, <b>text_field</b>, <b>text_area </b>etc. <br><br>The above will generate the following HTML:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_379csk5w5fq_b"><br><br>You can see from the above that the form is going to <b>post </b>to the <b>/expenses</b> url. This URL is identical to the <b>index </b>action url of the <b>ExpensesController</b> but you should also notice that the method of the form above is of type <b>post</b>. Rails maps the plural url and the post method to an action called create that is one of the seven default actions that rails creates for us with every resource we create. </div><br>Another item of noteworthy mention is how the formbuilder has constructed the name fields of the inputs that you can see in the above html. For example the checkbox for the Paid by Director field has a name attribute of <b>expense[paid_by_director]</b> which has been generated from the check_box method:<br><br><b>=f.check_box :paid_by_director, { :class => 'check' }</b><br><br>When we submit this form rails will use nesting to nest the <b>paid_by_director </b>attribute inside a hash called <b>expense</b>. Rails uses this to group the attributes of a single model inside a single hash.<br><br>It is also worth noting that for the checkbox, the formbuilder has generated 2 inputs for the <b>f.check_box</b> call. One input is obviously of type checkbox but the other is a hidden field. The hidden field is used to record the state of the checkbox when it is posted. If a checkbox is not checked then it will not appear in the form post collection. In this case rails will use the value in the hidden field.<br><br>When the form is submitted, the form inputs will be bundled into the params hash. <br><br>As I have mentioned in the last couple of posts, we are being good little TDD soldiers and because of this, we create the following <b>functional test</b> or <b>controller test </b>to test the create action of the <b>. </b>I introduced functional testing in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" id="ba77" title="post">post</a>. <br><br>We create the following test to test passing the form fields to the form:<br><br><div id="e76:" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_383hmzx7rcj_b" style="height:241px;width:648px"><br><br><br></div>This is exactly the type of hash that gets routed to the <b>create</b> action of our <b>ExpensesController </b>from the above HTML form that is generated from our HAML.<br><br>If we want to know exactly what values are being passed to our <b>create </b>action, we can create add the following code to the create action:<br><br><div id="c4ju" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_384ckgv89dm_b" style="height:111px;width:447px"></div>Which will display the values of the params hash that is created when the submit button is clicked and should display something like the following in your browser of choice:<br><br><div id="v5rc" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_38594sfbbdr_b" style="height:143px;width:648px"></div>Another way of doing this is to change the action to the following:<br><br><div id="l9lj" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_386f4k2dnfg_b" style="height:156px;width:421px"></div>Here we are raising an exception and calling the to_yaml method of the params hash to display the details of the hash hierarchically:<br><br><div id="e748" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_387d8ms6sdf_b" style="height:320px;width:648px"></div><br>This will of course cause our test to fail so we will fall back to the original.<br><br>We have now tested that our create expense form is hooked up correctly. We now need to add the code to complete the deed of creating a new expense. We do this test first.<br><br>We update the previously created functional test to the following which is pretty reflective of a full functional test:<br><br><div id="trwt" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_389gv293fc5_b" style="height:487px;width:648px"></div><br>We are using flexmock to mock out the ActiveRecord calls as I am not a great believer in writing tests that run against the database.<br><br>We mock out the class methods of the Expense object to return exactly what we want to drive our tests in a more logical manner.<br><br>We then write the code to make the create action pass:<br><br><div id="g8kh" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_390jz9hjtf4_b" style="height:248px;width:648px"></div>From the above, you should notice the following:<br><br><ol><li>We are creating a new Expense instance from the <b>params </b>hash that contains the form fields. We are assigning this new object to the <b>@expense </b>instance variable. This technique of setting multiple attributes at one time on a model is called <b>mass assignment</b>. </li><li>The ActiveRecord save method returns a boolean that indicates whether the save was successful or not. </li><li>In the event of a successful save, we are placing a <b>notice </b>in the <b>flash provider </b>and redirecting the index action which we access using the short cut convention <b>expenses_url</b>. The <b>flash </b>provider is a way to pass temporary objects between actions. Anything you place in the flash will be exposed to the very next action and then cleared out. This is a great way of doing notices and alerts. I seem to remember something similar in Castle.Igloo that later became Rhino.Igloo.</li><li>If the save was not successful, we redirect back to the new action.</li></ol><br>One problem that is often ignored with <b>mass assignment </b>is that any method can be called from a model that is contained in the <b>params </b>hash including database calls. In order to prevent this we add the <b>attr_accessible </b> method to our model and define the methods that we want editable. To accomplish this, we update the Expense model class:<br><br><div id="w117" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_394gvj94cft_b" style="height:104px;width:648px"></div><br>If we the save is successful, the browser will be redirected to the index action that displays all the Expenses. We need to update the code to actually render the expenses from the database.<br><br>In order to do this, we add a new functional test that checks that the index method runs with records in the database:<br><br><div id="jxwl" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_3968thrsnkq_b" style="height:210px;width:648px"></div><br>Now we update our index.html.haml to the following:<br><br><div id="ph0f" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_397dzt3tv9n_b" style="height:404px;width:648px"></div><br><br>If we successfully insert a record, we are greeted with the following confirmation in the expenses index view:<br><br><div id="uorq" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_398dhnsj8c2_b" style="height:153px;width:648px"></div>This post is much longer than I intended so I will leave things here.<br><br>In the next post, I will illustrate how to save the Expense as a whole. This will include the child objects that are part of the complex expense object.<br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-58102260820048352602010-03-30T00:35:00.003-07:002010-05-12T06:41:44.733-07:00Rails - Day 13 -ActiveRecord Relationships Part II<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last post, we started to define the relationships for a mock expenses tracking application that you can clone from github <a href="http://github.com/dagda1/expensetracker" id="zmal" style="color:#551a8b" title="here">here</a>.<br><div><br>We are creating a pseudo application to record the expenses that might incur for a small business.</div><br><div>We have created a mock UI below which we are using to drive the structure of the <b>Expense</b> model.<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_3032h5k8kg4_b"><br><br>In the last post, we defined how an <b>Expense</b> model object is associated with an <b>ExpenseType</b> model object.</div><br><div id="kle2" style="text-align:left">In the screenshot above, you can see that there are 3 inputs for the <b>Net</b>, <b>Vat</b> and <b>Gross </b>values of every expense that is entered into the system.<br><br>An <b>ExpensePayment</b> model object will be created to capture this logic and will be associated with the parent <b>Expense</b> model object. The next step is to generate the files that will aid us in our endeavour of creating an <b>ExpensePayment </b>model with the following command:<br><br><b>ruby script/generate model ExpensePayment</b> <br><br>This generates the following files:<br><br><div id="rkds" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_333sj5jbrf8_b" style="height:253.494px;width:648px"></div><br>We want to define the usage of this new object before we create the class itself. <br><br>The generator has kindly hinted that this a good idea and has created a <b>test/unit/expense_payment_test.rb </b>file which we will update with the following test case to ensure that we can at least store and display the correct payment attributes in the format we expect:<br><br></div><div id="ykw5" style="text-align:left"><div id="dhab" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_381f8xntxfb_b" style="height:300.449px;width:648px"></div><br><br></div><div id="h_u9" style="text-align:left">One thing of note is the crazy <b>include ActionView::Helpers </b>statement which appears after the class declaration. The <b>include </b>method will <b><i>mixin</i></b> a module's methods at the instance level that will become methods of the instance. You can see above that we have 2 calls to <b>number_to_currency </b>at the end of the test case. The <b>number_to_currency</b> method is mixed in to the <b>ExpensePaymentTest</b> class instance from the <b>ActionView::Helpers</b> module as is defined in the <b>include</b> statement. <b><i>Mixins</i></b> are ridiculously easy in ruby compared to the hoops you have to jump through in C# to achieve a similar experience. As the methods suggest, they will format the two totals from the ExpensePayment object.<br><br>This test will not pass as the <b>ExpensePayment</b> object has not yet been created but as we are using TDD, the behaviour of the object can be defined up front before running the migration. If we look at the UI at the top of the page, you should see 3 input fields for <b>net</b>, <b>vat </b>and <b>gross</b>. It is safe to say that only <b>net </b>attribute needs persisted to the database as we can derive both the <b>gross </b>and the <b>VAT </b>incurred from the <b>net </b>total. You can see below how we set this value in the above code with the lines:<br><br><div><b>payment = ExpensePayment.new</b></div><br><div><b>payment.net = 3.23</b></div><div><b>payment.vat_at_payment = 17.5</b></div><br>We are also storing a <b>vat_at_payment</b> attribute because if you live in a crazy mixed up place like the UK, the VAT rate might vary from month to month as the government tries to recoup all the money it has spent dishing out to banks and waging wars against third world countries. </div><div id="nsbs" style="text-align:left"><br></div><div id="skk2" style="text-align:left">We then create the following migration to create the <b>expense_payments </b>table. The <b>expense_payments</b> table will only contain the <b>net </b>and <b>vat_at_payment </b>fields:<br><br><div id="ng1j" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_375cscq2bfb_b" style="height:254.661px;width:648px"></div><br><br>That still leaves the <b>vat </b>and <b>gross </b>attributes which are formatting to assert their values in the above test. The model is updated to add these attributes:<br><br></div><div id="fhp5" style="text-align:left"><div id="x73s" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_449c59g67hg_b" style="height:589px;width:513px"></div><br><br>If we run the above test, it now passes. We have created a usable <b>ExpensePayment </b>object using the power of TDD. We should of course create test cases for nil objects but I will leave that as an exercise for you the reader.</div><div id="i510" style="text-align:left"><h2>belongs_to and has_one newbie Confusion</h2>We now want to define the relationship between <b>Expense</b> and <b>ExpensePayment</b>.<br><br>In the last post, we illustrated the ActiveRecord <b>belongs_to</b> relationship. There is a probably lesser known <b>has_one</b> relationship that to be honest, I had a bit of a problem getting my head around. Searching the web for answers seemed to suggest this is a bit of a grey area for newbies like myself. The <b>belongs_to </b>relationship seems to be a better fit when linking to pre-existing objects. For example, in the previous post, it was stated that an <b>Expense</b> belongs_to <b>ExpenseType</b>. The <b>ExpenseType</b> objects were created before linking the <b>Expense </b>to the <b>ExpenseType</b>.<br><br>So in this example, we are going to say that an <b>Expense has_one</b> <b>ExpensePayment</b>. <b>has_one</b> makes more sense in this scenario because when we create an <b>Expense</b>, we are also creating an <b>ExpensePayment </b>object<b> </b>at the same time. </div><div id="x19o" style="text-align:left"><br>As always, we write a test to flesh out our desired behaviour:<br><br><div id="vmig" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_450hskvhbgw_b" style="height:299.565px;width:648px"></div><br><br>Next a migration is created to add the <b>expense_id </b>foreign key to the <b>expense_payments </b>table with the command:<br><b><br>ruby script/generate migration add_expense_id_to_expense_payment</b><br><br>This newly created migration is updated to the following:</div><div id="bmpl" style="text-align:left"><br></div><div id="hb82" style="text-align:left"><div id="vkix" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_339dx4s56gk_b" style="height:230.371px;width:648px"></div></div><div id="y..o" style="text-align:left">The development and test environments are migrated with the following consecutive commands: <br><b><br>rake db:migrate<br>rake environment RAILS_ENV=test db:migrate</b><br><br>Then the <b>Expense </b>model is updated to the following:<br><br><div id="fdym" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_340gvmqjscr_b" style="height:160px;width:622px"></div></div><div style="text-align:left">And lastly the <b>ExpensePayment </b>model is updated to the following:<br><br><div id="rtks" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_341ddxvmfdt_b" style="height:101.578px;width:648px"></div><br>This slightly confusing and contradictory relationship is now defined. The tests now pass and we can progress to the UI.<br><br>That is it for ActiveRecord relationships which can be slightly ambiguous in places. I have not mentioned <b>many to many</b> relationships but there are exactly 1,043,293 examples on the web for you to discover. </div><div style="text-align:left"><br>Next up, I want to mention forms in Rails.<br><br><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-16802002715474149332010-03-30T00:35:00.001-07:002010-03-30T00:35:52.095-07:00Rails - Day 13 -ActiveRecord Relationships Part II<div>So far in the series we have:<br><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" id="z5n0" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a><br><br>In the last post, we started to define the relationships for a mock expenses tracking application that you can clone from github <a href="http://github.com/dagda1/expensetracker" id="zmal" style="color:#551a8b" title="here">here</a>.<br></div><div><br>We are creating a pseudo application to record the expenses that might incur for a small business.</div><br><div>We have created a mock UI below which we are using to drive the structure of the <b>Expense</b> model.<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_3032h5k8kg4_b"><br><br>In the last post, we defined how an <b>Expense</b> model object is associated with an <b>ExpenseType</b> model object.</div><br><div id="kle2" style="text-align:left">In the screenshot above, you can see that there are 3 inputs for the <b>Net</b>, <b>Vat</b> and <b>Gross </b>values of every expense that is entered into the system.<br><br>An <b>ExpensePayment</b> model object will be created to capture this logic and will be associated with the parent <b>Expense</b> model object. The next step is to generate the files that will aid us in our endeavour of creating an <b>ExpensePayment </b>model with the following command:<br><br><b>ruby script/generate model ExpensePayment</b> <br><br>This generates the following files:<br><br><div id="rkds" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_333sj5jbrf8_b" style="height:253.494px;width:648px"></div><br>We want to define the usage of this new object before we create the class itself. <br><br>The generator has kindly hinted that this a good idea and has created a <b>test/unit/expense_payment_test.rb </b>file which we will update with the following test case to ensure that we can at least store and display the correct payment attributes in the format we expect:<br><br></div><div id="ykw5" style="text-align:left"><div id="dhab" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_381f8xntxfb_b" style="height:300.449px;width:648px"></div><br><br></div><div id="h_u9" style="text-align:left">One thing of note is the crazy <b>include ActionView::Helpers </b>statement which appears after the class declaration. The <b>include </b>method will <b><i>mixin</i></b> a module's methods at the instance level that will become methods of the instance. You can see above that we have 2 calls to <b>number_to_currency </b>at the end of the test case. The <b>number_to_currency</b> method is mixed in to the <b>ExpensePaymentTest</b> class instance from the <b>ActionView::Helpers</b> module as is defined in the <b>include</b> statement. <b><i>Mixins</i></b> are ridiculously easy in ruby compared to the hoops you have to jump through in C# to achieve a similar experience. As the methods suggest, they will format the two totals from the ExpensePayment object.<br><br>This test will not pass as the <b>ExpensePayment</b> object has not yet been created but as we are using TDD, the behaviour of the object can be defined up front before running the migration. If we look at the UI at the top of the page, you should see 3 input fields for <b>net</b>, <b>vat </b>and <b>gross</b>. It is safe to say that only <b>net </b>attribute needs persisted to the database as we can derive both the <b>gross </b>and the <b>VAT </b>incurred from the <b>net </b>total. You can see below how we set this value in the above code with the lines:<br><br><div><b>payment = ExpensePayment.new</b></div><br><div><b>payment.net = 3.23</b></div><div><b>payment.vat_at_payment = 17.5</b></div><br>We are also storing a <b>vat_at_payment</b> attribute because if you live in a crazy mixed up place like the UK, the VAT rate might vary from month to month as the government tries to recoup all the money it has spent dishing out to banks and waging wars against third world countries. </div><div id="nsbs" style="text-align:left"><br></div><div id="skk2" style="text-align:left">We then create the following migration to create the <b>expense_payments </b>table. The <b>expense_payments</b> table will only contain the <b>net </b>and <b>vat_at_payment </b>fields:<br><br><div id="ng1j" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_375cscq2bfb_b" style="height:254.661px;width:648px"></div><br><br>That still leaves the <b>vat </b>and <b>gross </b>attributes which are formatting to assert their values in the above test. The model is updated to add these attributes:<br><br></div><div id="fhp5" style="text-align:left"><div id="x73s" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_449c59g67hg_b" style="height:589px;width:513px"></div><br><br>If we run the above test, it now passes. We have created a usable <b>ExpensePayment </b>object using the power of TDD. We should of course create test cases for nil objects but I will leave that as an exercise for you the reader.</div><div id="i510" style="text-align:left"><h2>belongs_to and has_one newbie Confusion</h2>We now want to define the relationship between <b>Expense</b> and <b>ExpensePayment</b>.<br><br>In the last post, we illustrated the ActiveRecord <b>belongs_to</b> relationship. There is a probably lesser known <b>has_one</b> relationship that to be honest, I had a bit of a problem getting my head around. Searching the web for answers seemed to suggest this is a bit of a grey area for newbies like myself. The <b>belongs_to </b>relationship seems to be a better fit when linking to pre-existing objects. For example, in the previous post, it was stated that an <b>Expense</b> belongs_to <b>ExpenseType</b>. The <b>ExpenseType</b> objects were created before linking the <b>Expense </b>to the <b>ExpenseType</b>.<br><br>So in this example, we are going to say that an <b>Expense has_one</b> <b>ExpensePayment</b>. <b>has_one</b> makes more sense in this scenario because when we create an <b>Expense</b>, we are also creating an <b>ExpensePayment </b>object<b> </b>at the same time. </div><div id="x19o" style="text-align:left"><br>As always, we write a test to flesh out our desired behaviour:<br><br><div id="vmig" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_450hskvhbgw_b" style="height:299.565px;width:648px"></div><br><br>Next a migration is created to add the <b>expense_id </b>foreign key to the <b>expense_payments </b>table with the command:<br><b><br>ruby script/generate migration add_expense_id_to_expense_payment</b><br><br>This newly created migration is updated to the following:</div><div id="bmpl" style="text-align:left"><br></div><div id="hb82" style="text-align:left"><div id="vkix" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_339dx4s56gk_b" style="height:230.371px;width:648px"></div></div><div id="y..o" style="text-align:left">The development and test environments are migrated with the following consecutive commands: <br><b><br>rake db:migrate<br>rake environment RAILS_ENV=test db:migrate</b><br><br>Then the <b>Expense </b>model is updated to the following:<br><br><div id="fdym" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_340gvmqjscr_b" style="height:160px;width:622px"></div></div><div style="text-align:left">And lastly the <b>ExpensePayment </b>model is updated to the following:<br><br><div id="rtks" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_341ddxvmfdt_b" style="height:101.578px;width:648px"></div><br>This slightly confusing and contradictory relationship is now defined. The tests now pass and we can progress to the UI.<br><br>That is it for ActiveRecord relationships which can be slightly ambiguous in places. I have not mentioned <b>many to many</b> relationships but there are exactly 1,043,293 examples on the web for you to discover. </div><div style="text-align:left"><br>Next up, I want to mention forms in Rails.<br><br><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-1012792160647243972010-03-29T01:18:00.001-07:002010-05-12T06:41:24.336-07:00Rails - Day 12 -ActiveRecord Relationships Part I<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" id="qgxl" title="post">post</a>, I touched briefly on unit testing and ActiveRecord.<br><br>I also mentioned that we are driving the model from the UI that has been created before the model:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_3032h5k8kg4_b"><br><br>I also confessed that the tight coupling to the database which is a result of the ActiveRecord pattern did not make me feel too good but is something I have learned to swallow in the name of productivity. I will illustrate some of those productivity gains in this post that make the coupling compelling.<br><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" id="oaci" title="post">post</a> I created a migration and a test to create the expense object.<br><br>The <b>Expense </b>model object only encapsulates the <b>Paid by Director</b>, <b>Expense Date</b>, <b>External Doc Ref</b> and <b>Posting Description</b> from the above screenshot.<br><br>In this post, we will also create an <b>ExpenseType </b>model object and in the next post we will create an <b>ExpensePayment </b>model object that will be used to record our expense details. <br><br>This is how our database schema should look when when the relationships have been defined:<br><br><div id="sl6o" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_355d6hw2sfg_b" style="height:186px;width:648px"></div><br><br><div>If you look at the screenshot of the UI at the beginning of the post, you can see that there is an Expense Type dropdown. Every expense that is created must have an expense type associated with it. An example of an expense type might be travel costs or office equipment.<br><br>The first step is to create the association between expense and expense type.<br><br>The characteristics of the Expense and ExpenseType relationship are:<br><br></div><ul><li>Every <b>Expense</b> will have one <b>ExpenseType </b>associated with it. </li><li>Each <b>ExpenseType </b>can have many <b>Expenses</b> associated with it.</li></ul><br>If you are familiar with Nhibernate, it can be put into the following terms:<br><br><ul><li>An expense has a <b>many-to-one</b> relationship with <b>ExpenseType.</b></li><li>An <b>ExpenseType </b>will have a <b>one_to_many</b> relationship with a <b>Expenses</b>.</li></ul><br>We now need to create an <b>ExpenseType</b> model object. In order to generate the appropriate model files, the following command is entered into the console:<br><br><div><b>ruby script/generate model ExpenseType</b> which creates the following files:<br><br><div id="m67y" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_313mbtxxncq_b" style="height:224px;width:648px"></div><br><b>Note</b> that it is<b> a model </b>being generated and not a <b>resource</b>, no controller has been created for us and no routing has been updated.<br><br>The newly updated migration is updated to incorporate the attributes that will be required to record an expense type:<br><br><div id="igas" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_3195qfvk77_b" style="height:348px;width:648px"></div><br><br>The <b>db:migrate</b> command will create the <b>expense_types </b>table. I am not going to show the tests for this creation process, you will just have to take my word for it!! <br><br>In a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" id="jlyq" title="post">post</a>, I mentioned how it was possible to seed the database with start up data by writing ruby script in the <b>db/seeds.rb</b> file. In order to seed our database with some expense types, the following code is added to the <b>db/seeds.rb </b>file:<br><br><div id="i6em" style="text-align:left"><div id="jl9v" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_357chbv5kct_b" style="height:75px;width:648px"></div><br>You can see here we are using the ActiveRecord <b>find_or_create_by_name </b>which will check for the existence an <b>ExpenseType </b>with the name that is passed to the function as an argument and if none is found, the record is created. What you might not realise is that this method does not actually exist and is in fact an example of Ruby/ActiveRecord magic. ActiveRecord supplies some convenience methods to find records by their attribute values. These methods are called <b><i>dynamic finders</i></b>. There are two forms of dynamic finder, <b>find_by_xxxx</b> and <b>find_or_create_by_xxxx</b> to create a record if you cannot find it. If the <b>ExpenseType </b>class had an attribute named code, we could call a method named <b>find_or_create_by_code</b>. We could also define the two fields to call a method named <b>find_by_name_and_code</b>. This is fairly mind blowing stuff but when you actually look into the mechanics it seems less surreal. If you are familiar with Ruby then it probably won't come as a great surprise to learn that this magic is carried out in the ruby catch-all for unknown methods called <b>method_missing:</b></div><div id="zti_" style="text-align:left"><br><div id="zvua" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_376hdt5fqdc_b" style="height:173px;width:648px"></div><br><br></div>These posts are not about Ruby but ActiveRecord uses this technique to create many aesthetically pleasing convenience methods that we can utilise in order to both query and execute modifications against the database. Much of the <i>magic </i>that occurs in Ruby takes place in <b>method_missing</b>.<br><br>Getting back to the <b>seeds.rb</b> file, this pseudo method, will create the record in the database if it encounters an unknown value for the name value. What should not escape your attention that this is all happening in one line of code. All the plumbing is happening in the framework. It is examples like this that make me feel less queasy about the tight coupling between the database and the model.<br><br>The easiest way to see what records are in the database is to spark up the excellent rails console that runs in the context of our application. If we enter the following command <b>ruby script/console</b>, we can execute ruby commands in the context of our current project. Below, you can see I am typing <b>ExpenseType.all </b>to query the expese_types table and display the data that has been added via the <b>seeds.rb</b> file:<br><br><div id="s6or" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_324ffqsxccs_b" style="height:112px;width:648px"></div><br><h2>ActiveRecord Relationships</h2>So far, we have created the <b>Expense</b> object, and also created and seeded the <b>ExpenseType</b> object but how is the relationship defined between the two objects? Remember, we are saying that an <b>Expense </b>has one associated <b>Expense Type</b>.<br><br>In rails ActiveRecord, the relationship is expresssed as saying that an <b>Expense belongs_to </b>an<b> ExpenseType</b>. <b>belongs_to</b> specifies a one to one association with another class. This method should only be used if the class <b>belonging to</b> the other class contains the foreign key.<br><br>There is of course a convention for this foreign key relationship that negates the need to specify it anywhere in configuration or code. The convention is that the foreign key takes the form of the name of the model followed by an underscore and id. So in this case we will add a new field to the <b>expenses </b>table called <b>expense_type_id</b>.<br><br><div>As I mentioned in the last post, we are driving out our model test first using TDD like the good little ALT.NETters that we are and create the following test:<br><br></div><div id="i7u1" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_360gnv7nv8n_b" style="height:377px;width:648px"></div><br><br>Obviously, this test will fail as the database schema has not been updated and the relationship has not been defined.<br><br>The first step is to create a new migration. A migration is create with the following command: <br><b><br>ruby script/generate migration add_expense_type_to_expense</b>:<br><br><div id="n30q" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_325g46pfxdn_b" style="height:139px;width:648px"></div><div id="m6_6" style="text-align:left"><br></div><div id="k6_i" style="text-align:left">The following code is added to the newly created migration file:<br><br><div id="gfjd" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_326ggndtnxm_b" style="height:229px;width:648px"></div><br></div>I run the migration with the <b>rake db:migrate</b> command.<br><br>There is one gotcha that has caught me out more than once and left me howling at the moon and to the Gods. In a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" id="lwki" title="post">post</a>, I mentioned rails environments When we run the comand <b>rake db:migrate</b>, the migration is ran against the environment we are currently in which more than likely is not the test environment. To run the migration against the test environment, we can either change environments by entering the command <b>set RAILS_ENV = test </b>or we can complete the task in one line as we are doing below with the command:<br><br><b> rake environment RAILS_ENV=test db:migrate:<br></b><br><div id="u_ln" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_329gkqshkdf_b" style="height:121px;width:648px"></div><br>We are not done yet and we need to add some code to our model classes to specify the relationship.<br><br>The <b>Expense </b>model<b> </b>class is updated to the following:<br><br><div id="wyeq" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_361c2kmj6dk_b" style="height:128px;width:606px"></div><br><br>The <b>ExpenseType </b>model class is updated to the following:<br><br><div id="ex_g" style="text-align:left"><div id="ir.p" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_363f8vphtcm_b" style="height:135px;width:648px"></div><br></div><div id="jaq_" style="text-align:left"><br>As aesthetics are really important with rails, the above <b>belongs_to </b>and <b>has_many </b>macros leave little to the imagination as to their true meaning. <br><br>The above test can now be run with the command <b>rake test:units </b>or by by pressing <b>ctrl + F9</b> in RubyMine.<br><br>In RubyMine we get the nice green bar that we all love and cherish:<br><br><div id="pn75" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_362f8s63dg4_b" style="height:301px;width:648px"></div><br>In the next post, I will illustrate the relationship between the <b>Expense </b>model object and the yet to be created <b>ExpensePayment</b> model object. <br><br><br></div><div id="ohpa" style="text-align:left"><br><br></div><div id="g.79" style="text-align:left"><br></div><br><br><br><br><br><br><br><br><br></div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com1tag:blogger.com,1999:blog-5221465892382713395.post-31664136320005441762010-03-25T22:54:00.001-07:002010-05-12T06:40:17.356-07:00Rails - Day 11 - Model Testing and ActiveRecord 101<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" id="p7gm" title="post">post</a>, we touched on rails controller testing, and now in this post, I want to touch on Model testing.<br><br>In Rails, unit testing is what you do when you test your models. Model testing is where we should write the majority of our tests. <br><br>The mantra <b>Fat Models and skinny Controllers</b> is one often associated with the MVC pattern and a controller action that does <b>too much </b>obscures the intention of the action. A controller action should be intention revealing. I start to get suspicious of my controller actions if they start stretching over five lines of code. Often my actions are just one line of code like below:<br><br><div id="d5dv" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_353dgxzhwcg_b" style="height:128.925px;width:648px"></div><br><br>As I mentioned in the last post, we have created a fictitious application to record the expenses that are incurred when running a business. Examples of such expenses are Travel Expenses or Office equipment.<br><br>It is a fair assumption to make that we will need an <b>Expense</b> resource abstraction to model this real world entity and we have previously generated the rails ruby files that we will need with the <b>ruby script/generate</b> <b>resource expense</b> command which has generated the following files:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_3506c6d75gt_b"><br><br>If this was a real application that I was working on for a real client then I would create the UI <b>first </b>and then think about the model. I find that driving the UI out first really helps with defining the structure of my model. It also gives me a shorter feedback loop with the client. <br><br>I have done the same for this silly application and below you can the page that we will use to both create and update an individual expense item:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_3032h5k8kg4_b"><br><br>I can assume from the UI above, that the <b>Expense</b> model object is a complex object, meaning the expense object will have child objects that define its structure.<br><br>I am going to leave the discussion about defining relationships between objects in ActiveRecord until the next post.<br><br>For this post, we are going to create the Expense object itself using TDD. As previously stated, we have ran the generate script to add an expense resource that has created and stubbed out a number of files for us.<br><h1>ActiveRecord, Migrations and Models</h1><div>ActiveRecord is the <a href="http://en.wikipedia.org/wiki/Object-relational_mapping" id="un0m" title="ORM (Object Relational Mapper)">ORM (Object Relational Mapper)</a> layer used in rails. It is used by controllers as a proxy to the database tables. The obvious advantage of an ORM is that it saves you from having to write tedious SQL. <br><br>The basic concept in ActiveRecord is the model. Each model class is stored in the <b>app/models</b> directory inside your application, in it's own file. In our application, we have a model file in <b>app/models/expense.rb </b>that was created by the script generator:<br><br><div id="jc6b" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_308fbdx6rf8_b" style="height:79px;width:648px"></div><br></div>Each model will generally correspond to a table in the database. The name of the table is by convention, the pluaralised, lower-case form of the model's class name. So in our case, we have an <b>Expense </b>model and an <b>expenses</b> database table.<br><br>I mentioned migrations in a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" id="nqye" title="post">post</a> and the <b>ruby script/generate</b> <b>resource Expense</b> command has created for us a create migration for the expense resource:<br><br><div id="zbck" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_304hdgzf2g4_b" style="height:361px;width:648px"></div><br><div>You can see from the above that only the default fields have been created. If we run <b>rake db:migrate</b> then the <b>expenses </b>table will be created. We have already done this in order to create just enough server code to get our UI working. Migrations are part of ActiveRecord and enable a rails developer to generate a database structure using a series of Ruby script files.</div><br>Using the UI screenshot as a guide, we are going to write a test to ensure we create the correct fields for the expense model.<br><br>As we are going to be adding fields onto our model, we are going to create a unit test. The script generator has kindly created for us a unit test file for this very purpose in /<b>test/unit/expense_test.rb</b>.<br><br><div id="q_c4" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_305gjcxr2db_b" style="height:300px;width:384px"></div><div id="tf58" style="text-align:left"><br></div><div id="iewc" style="text-align:left">So far this test file only contains one default test case which we will remove.<br><br>Using the UI as our guide, we want to test that we can record the relevant information of the <b>Expense</b> model object.<br><br>We want to record the following:<br><br><ul><li>Whether the expense was paid by a director. we will create a <b>paid_by_director</b> field which will be of type <b>boolean</b>. </li><li>The date the expense was paid. We will create a <b>expense_date </b>field of type <b>date</b>. </li><li>An external reference identifier we can use to tie our receipt to the expense. We will create an <b>external_reference</b> field of type <b>string</b>. </li><li>A long description of the what the expense was. We will create a <b>description</b> field of type <b>string</b>.</li></ul><br><div>We will need other objects to record the other data in the screen shot to store the entire expense information. We will cover relationships between objects in the next post. For now, we will just work on the simple expense model.<br><br>As we are good TDD soldiers and do not want our ALT.NET friends scoffing at us at the next open spaces forum, we want to write our model test code first. What this means is, we will write the test code before the application code. In all seriousness, TDD has saved me many times from disappearing down very dark rabbit holes. With TDD, you <b>first</b> write a failing test and then write enough code to make the test pass. This seriously keeps you focused on the job in hand. I have also found this a great way of inspiring myself to provide a solution when faced with writers' block.<br><br>Using the UI as my guide, I write the following test:<br><br></div><div id="en3n" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_310hkw3vpcv_b" style="height:472px;width:648px"></div><br><br>As I mentioned in the previous post, we are using rake to run our tests and we can use rake to run only the unit test of our project with the command <b>rake test:units</b>.<br><br></div>As expected, the test fails with the following confirmation:<br><br><div id="r4zh" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_307dbsdppc6_b" style="height:282px;width:648px"></div><br>We can see here that it is telling us we have an undefined method paid_by_director which is absolutely true.<br><br>We will now add the fields to the object. <br><h1>Table first or Model First?</h1>In .NET and with Nhibernate, the way I like to work is to first drive out the model using TDD, I then create the mapping files and lastly, I run <a href="http://ayende.com/Blog/archive/2008/02/09/Setting-Up-Zero-Friction-Projects--Data-Access.aspx" id="vhmr" title="SchemaUpdate">SchemaUpdate</a> to generate the database structure. In rails it is slightly different in that you define the database tables first. The model then picks up these fields that are added to the table by convention. To be perfectly honest, the Nhibernate way feels like the more pure and righteous path but the productivity gains of rails make me feel less dirty about the whole thing. The coupling to the database as a result of the ActiveRecord pattern is something I have learnt to feel less bad about. In my heart of hearts I know such a coupling is bad but the productivity hit is good. The scales of justice swing to the right for me on this one. <br><br>In order to add these new fields to our expense database table that will enable our test to pass, we are going to have to create a new migration to add the columns. We do this with the <b>ruby script/migrate add_initial_fields_to_expenses</b> command which creates a file in <b>db/migrate/20100321234831_add_initial_fieilds_to_expenses.rb</b>.<br><br>In this newly created migration file, we add the following text:<br><br><div><img src="http://docs.google.com/File?id=dgjpt2xx_309gwsxq8dr_b"><br><br>We now run the migration with the <b>rake db:migrate</b> command.<br><br>We are now ready to re-run our tests with the <b>rake test:units</b> command. It should be no surprise that the unit test and assertions now pass:<br><br><div id="tj-z" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_311tn8rhp_b" style="height:90px;width:455px"></div><div id="qhwk" style="text-align:left"><br></div><div id="y-a6" style="text-align:left"><h1>Avoid Meaningless Tests</h1>Below is an example of what on the surface seems a reasonable thing to test but in reality is a bit of a waste of time.<br><br><div id="q03o" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_354krwx6d7_b" style="height:162.616px;width:648px"></div><br></div><br>The above is a bit of a silly test because you are not testing your own code. You are in affect testing the ActiveRecord library which I can say with some degree of confidence is pretty damn well tested.<br><br><div>We should be testing model logic, not the ActiveRecord framework. You should be testing code that <b>you</b> write. Anything that gets added to your model needs test coverage.<br></div><h1>Mocking</h1><div>I lastly want to touch on mocking in Rails. Before I discovered mocking, my tests were always written against resources such as the database or a webservice call. Such tests are brittle in the extreme.<br><br>In the last post, I covered writing functional tests that we use for covering controller actions. <font size="2">I also stated that, the </font><font size="2"><b>index</b></font><font size="2"> action of a controller is generally used to display a list or subset of the requested resource. This would 999 times out of 1000 require a call to the database. The index action of our <b>ExpensesController </b>would probably look like the following:<br><br></font></div><div style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_295df8z7kcv_b" style="height:167px;width:648px"></div><br>As I stated earlier, testing the <b>all</b> method of the ActiveRecord framework is pretty senseless as we want to test code that <b>you</b> write and not framework code. But with this functional test, we are testing that we get the correct HTTP repsonse and that the correct template file is selected.<br><br>As these are functional tests, I am unsure what the correct party line is here, would you mock these calls or would make the calls directly to the test environment database? If anybody is reading this and has an opinion on this, they can please leave a comment. I am personally not a huge believer in integration tests and the maintenance involved, so I generally write very few of these. We can always use an in memory database for our test environment like SQLLite but I still prefer to mock the majority of these calls.<br>Anyway, for the sake of this example, I am going to mock this call to the database. I am going to use FlexMock as my mocking library of choice which is easily installed via the beautiful RubyGems with the command gem install flexmock.<br>Next, I update the previously mentioned test to the following:<br><br><div style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_3005dx9g6gp_b" style="height:402px;width:648px"></div><br>The line of interest is the following:<br><br><div style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_301gt9jm6cs_b" style="height:64px;width:648px"></div><br>Here we are telling the flexmock framework that we are expecting a call to <b>all </b>from the <b>Expense</b> class instance and when this happens an empty array should be returned. It really is as easy as that.<br><br>I will leave things there for now. There are a million tutorials about testing in Rails and you should dig deeper if intrigued.<br><br>In the next post, I am going to move onto defining relationships in ActiveRecord.<br><br><br><br><br><br><br><br><br><br></div><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-16832987008128482372010-03-24T23:19:00.001-07:002010-05-12T06:39:36.963-07:00Rails - Day 10 - Testing Rails Controller<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><div><br>In the past <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" id="xk3t" title="post">post</a>, I talked a little about rendering content.<br><br>In this post, I want to touch lightly on testing with respect to rails of course. I am going to start with Controller testing or as it is known in rails, functional testing. <br><br>If you are one of my ten or so readership then it is likely that you are a .NET developer. If you are somebody who aligns themselves with those super cool, pola neck wearing ALT.NET guys, then you have quite probably written a blog post along the lines of <b>If you are not practicising TDD, then you should have your arms and legs cut off</b>.<br><br>So with this frightening threat on our limbs made, we better ensure we are following the TDD mantra by writing test code before application code.<br><br>I also want to introduce a sample application that I am going to use to illustrate the concepts. I run my own business and keep a record of all my expenses in a google docs spreadsheet. I have often toyed with the idea of writing something to record these entries and this seems as good as time as any to create such an application.</div><br>I have created a project which is named rather predictably <b>expensetracker</b>.<br><br>When I am developing, I like to create the user interface as a first iteration. The code is basically stubbed out as I create the HTML skeleton of the site. This allows me to get feedback from the client after we have agreed on the wireframes and also before we have written any serious server side code. I then use the UI to drive the model of my application. This might seem like heresy to the DDD zealots of which there are many but I find this a much better medium for feedback than any user story that can be wriitten.<br><br>This skeleton of the site does have some server code, and I like to make sure the navigation of the code is running through the web framework I am using. <br><br>For the <b>expensetracker </b>application, I will obviously need a page to display a list of the expenses I have recorded thus far. Below is my mock up of the page that will list all my expenses. This is just plain HTML at this stage but we want to at least have this rendered from a Rails controller action and the HTML generated from a combination of HAML and SASS.<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_349c87vcsd2_b"><br><br>Using my amazing powers of deduction and having read and been particularly underwhelmed by that blue book that makes some devs go weak at the knees, I think an <b>expense</b> resource will be required at some stage.<br><br>With that said, I steadfastly progress further and generate an expense resource with the <b>ruby script/generate resource Expense</b> command which generates the following files for us:<br><br><div id="wacx" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_3506c6d75gt_b" style="height:273px;width:526px"></div><br>As I mentioned in this previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="ty66" title="post">post</a>, the <b>index</b> action of the resource's controller is generally used to display all or a subset of the resource in question.<br><br>So if I am being righteous to the God of TDD, I should write the test for the index action before I write the controller action. I also like to have some test cases in place to set an example for other developers working on the project.<br><h1>Testing Rails Controller Testing a.k.a. Functional Testing</h1>In rails writing tests that test the actions of a single controller is called writing functional tests for that particular controller. You can see below that the resource generator has created a stubbed out test file for us in test/functional/expenses_controller_test.rb.<br><div id="asup" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_286cddbm7rj_b" style="height:250px;width:390px"></div><br>Functional tests interact with actions in the Rails controllers. They can test what variables each action prepares for the view, can test errors that Rails might raise and test the HTML that would get rendered with the view.<br><br>We should test for such things as:<br><br><ul><li>Was the web request successful? </li><li>Was the user redirected to the correct page? </li><li>Was the user correctly authenticated? </li><li>Was the correct object stored in the response template? </li><li>Was the appropriate message displayed to the user in the view?</li></ul><br>Before I go any further, it should be noted that I am going to use the default test runner and not something like <a href="http://rspec.info/" id="j6x1" title="rspec">rspec</a>. I prefer the simplicity of the default test runner and I do not have much need for the extra fluff that comes with <a href="http://rspec.info/" id="e1d2" title="rspec">rspec</a> or some of the other more wordy clones.<br><br>So at this minute in time, all I want to do is test that I can get a success response and that the correct view template is selected from the <b>index</b> action of the <b>ExpensesController</b>. <br><br>Below is a test that does just that:<br><br><div id="l74y" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_288fzhhqbgw_b" style="height:277px;width:648px"></div><br>The above method simulates a request on the <b>index</b> action of the <b>ExpensesController</b> making sure that the request was successful and also that the correct template file is in place.<br><br>The <b>get</b> method kicks off a web request and populates the response. In our example we are just passing the name of the action as a symbol but there are 3 optional parameters we could use, for example if we were testing the <b>show </b>action, we could pass in an optional hash of request parameters into the action (e.g. query string parameters or post variables). An example of such a call would be:<br><br><b>get(:show, {'id' => "12", {'user_id' => 1})</b><br><br>Just as there is a get method, there are similar methods for the other HTTP verbs, <b>post, put, head and delete</b>.<br><br>So now we have written our first test, how do we test it? It is time for one of the main heroes of our piece to make a reappearance. I speak of <b>rake</b>. Many of the rails workflow commands are executed via rake and it is not just a simple build engine DSL which was my original impression. If we want to see what test commands are available through rake, we can simply type <b>rake -T test</b>, which will present us with the following:<br><br><div id="ohbs" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_289f7s5w4g3_b" style="height:286px;width:648px"></div><br>We can see from the above list, that there is a <b>rake test:functionals</b> command that will suit our needs. <br><br>Bearing in mind that we have neither created an action called index or created a template named <b>index.html.haml</b>, we would be in fairly big trouble if the test passed. <br><br>If we execute the <b>rake test:functionals</b>, we get the following message:<br><br><div id="gfac" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_290ccb563dt_b" style="height:251px;width:648px"></div><br>Here you can see, that the test does indeed fail and we are also told <b>ActionController::UnknownAction: No action responded to index</b>.<br><br>If we now add the folllowing action to our <b>ExpensesController </b>like this:<br><br><div id="fx9m" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_291gs77bmct_b" style="height:137px;width:648px"></div><br>We can rerun the tests with the same rake command and get the following response:<br><br><div id="ycte" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_292f6n3hzfn_b" style="height:277px;width:648px"></div><br>We can see that our test is failing because: <b>ActionController::UnknownAction: No action responded to index.<br></b><br>We can now add the template to the required destination of <b>app/views/expenses</b>.<br><br><div id="x45." style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_293f39x2mdv_b" style="height:200px;width:466px"></div><br>We now cross our fingers and rerun the test:<br><br><div id="wnul" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_2948wbhbwc7_b" style="height:265px;width:648px"></div><div><br>As we can see from the above, the test has ran and our 2 assertions have rightly passed.<br><br>Now we have the index rendering, we add the haml and sass for our UI mock up. <br><br>Below is my badly designed index page that will display all my expenses that is now being rendered from the index action. It should be noted that I am not rendering any content from the database.........yet.<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_349c87vcsd2_b"><br><br>If this was a work scenario, I would now go back to the client to check my assumptions before writing any more server side code.</div><h1 style="text-align:left">The New Action</h1>We will also need a page to record our expenses and as I mentioned in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="i4jf" title="post">post</a>, we use the <b>new</b> action to display a form for creating a new resource. In our example, the url will be <b>/expenses/new/</b>.<br><br>Again, as we are doing this test first, we create the following test:<br><br><div id="k4il" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_297gc7sbrgk_b" style="height:241px;width:520px"></div><br>We then add a blank new method to the <b>ExpensesControler </b>and a new view template at <b>app/views/expenses/new.html.haml</b> and run the test, we should be informed that all is good.<br><br>In this post, we have touched on controller testing.<br><br>In the next post, I want to touch on Model testing.<br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-90699212481941443342010-03-23T22:16:00.001-07:002010-05-12T06:38:59.005-07:00Rails - Day 9 - Rendering Content<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" id="r6x0" title="post">post</a>, we had a fairly in depth look at <a href="http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html" id="fxl4" title="HAML">HAML</a>, <a href="http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html" id="vb.s" title="SASS">SASS</a>, <a href="http://github.com/chriseppstein/compass" id="eyz:" title="Compass">Compass</a> and by association, the <a href="http://wiki.github.com/chriseppstein/compass/blueprint-documentation-grid-module" id="dbcf" title="blueprint CSS framework">blueprint CSS framework</a>.<br><br>In this post, I want to render some content that is dynamically rendered from data in our database. I know a lot of us do not like to admit that we work on applications that can be classified as CRUD applications but I fear a lot of us do. I very much include myself in this less than elite group.<br><br>In a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" id="g.:." title="post">post</a>, we generated a User resource using the <b>ruby</b> <b>script/generate resource User</b> command.<br><br>In another <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="uj_j" title="post">post</a>, I went on to explain how Rails uses REST to define several RESTful actions for each controller. The index action is generally used to list all or a subset of the resource in question, in our case Users. <br><br>We are going to change the index action of our UsersController to the following which will, as I hope is obviously return a list of users.:<br><br><br><div id="de2i" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_2562xb38ccw_b" style="height:125.25px;width:648px"></div><br>We will touch on the Rails ORM ActiveRecord later but I hope it is fairly obvious that we are returning all the Users in the users table and ordering the result set by the <b>user_name</b> field. One exciting thing of note is that we have not had to define an intermediary mapping file as we would with Nhibernate or the entity framework. <br><br>We have also defined an instance variable named <b>@users</b> that holds an array of User objects corresponding to all the User objects result set that is returned from the <b>find</b> method. Any instance variables we declare in the controller are available in the view which as previously stated, is by convention the view file with the same name as the controller action located in the subdirectory named after the resource. In the case of the Users resource index action, we have an index file in <b>app/views/users/index.html.haml</b>.<br><br>We are now going to refactor the <b>index.html.haml</b> file we updated at length in our last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" id="u_8y" title="post">post</a> to display the users objects that are be held in the <b>@users</b> instance variable, that is if there are any records:<br><br><div id="cgzq" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_348c4bzdqnt_b" style="height:389.501px;width:648px"></div><br>Again, I must stress that HAML is not everyone's cup of tea and the above might be abhorrent to you. I personally like its terseness. You should notice from the above that we are checking the length of the <b>@users</b> member variable we declared in our index action. If we have any elements in the array, we iterate over the @users array using ruby's block syntax. One thing of note in both the <b>if </b>statement and the <b>@users </b>iteration code is that there is no <b>end </b>keyword to mark the end of the code block. This is not required in HAML as significant whitespace is used to mark code blocks and regular ruby syntax would break these rules.<br><br><h1>Rails Layouts (Master Pages in .NET)</h1>In the last post, we split the content of our index.html.haml page into a header, sidebar, footer and content sections:<br><br><div><img src="http://docs.google.com/File?id=dgjpt2xx_250c4nfgnhj_b"><br><br>Obviously we do not want to cut and paste the header, sidebar and footer into each new view we create. <br><br>.NET has had master pages as of .NET 2.0 and in Rails, we have layouts. We are going to separate the common elements from our <b>index.html.haml </b>view and place them into a layout.<br><br>To do this, we create a new file named <b>application.html.haml</b> and place it in the designated place for layouts. That is right kids, layouts have, as is now hopefully predictable, their own convention. <br><br>The designated place is <b>app/views/layouts</b> folder:<br><br><div id="tmh:" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_260hsc6k3f5_b" style="height:300px;width:291px"></div><br></div><br>Our choice of layout file name has a reason behind it which we will get to later.<br><br>We then cut and paste the code from index.html.haml into application.html.haml. We will remove the code that iterates over the<b> @users</b> member variable and place it back into <b>index.html.haml</b>. We then replace this code in <b>application.html.haml</b> with the following:<br><br><div id="psbi" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_259dkf5qtgw_b" style="height:360.299px;width:648px"></div><br>We have replaced the code at the beginning of the post with the <b>=</b> <b>yield </b>syntax. <b>Yield </b>in this context will cause the entire contents of a view to be sucked into the layout before the page is rendered.<br><br>So how do we apply this layout to our view. As you might imagine, there are a number of ways of doing this in rails and not all of them requires writing any code:<br><br><ul><li>A controller specific layout will automatically be used if it exists. Each controller can contribute a file into the <b>app/views/layout </b>directory if it follows the specific naming convention of <b>${controller}.html.haml</b>. In our example, if we were to create a file in <b>app/views/layouts/users.html.haml</b>, then the layout will be applied automatically.</li></ul><br><ul><li>A site-wide default layout will be used if a controller-specific layout does not exist. The site wide layout-file must be named <b>application.html.haml </b>as we have done in this example. If the site wide layout file and the controller specific layout file both exist, the controller layout takes priority. This is the choice we have gone for in our example. All views will have this layout supplied but we can override it with controller specific layouts if we require.</li></ul><br><ul><li>You can specify the layout file in your controller or action code. </li></ul><br><h1>Rails Partials (User Controls)</h1>Partials are reusable bits of template code that can be included in a view. They are, as the heading suggests, analogous to User Controls in .NET and allow us to encapsulate and reuse UI behaviour in one place. <br><br>In order to illustrate this practice, we are going place the code that renders the HTML table below and is currently in index.html.haml and create a partial from it:<br><br><div id="m9qw" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_261f9nb4nd3_b" style="height:368.154px;width:648px"></div><br>Our first step is to create a file named _users.html.haml and place it in the <b>app/views/users</b> folder:<br><br><div id="nwet" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_263dgftqgf4_b" style="height:250px;width:434px"></div><br>All partials in rails must start with the underscore. We then cut and paste the table rendering code in the newly created partial.<br><br>We then update <b>index.html.haml</b> file to render the partial:<br><br><div id="z16p" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_264fns8fkff_b" style="height:115px;width:526px"></div><br>And that is it, easy peasy. I will not take the discussion on layouts and partials any further that this. Nested layouts are possible just as nested master pages are in .NET. Partials have many nuances as well that you can find out about if you are interested to know more.<br><h1>Rendering Xml or JSON</h1><i><span style="font-style:normal">On a final note, I want to mention how we can just as easily render JSON or XML. If we want to return JSON for example, we could change the index method to first of all retrieve all the users from the database and then return the result as JSON:</span><br><br></i><div style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_215hmvvvzcm_b" style="height:133.173px;width:648px"></div><br><i><span style="font-style:normal">This is achieved in one line of code which is pretty damn cool in my book. If we now call the index action through the browser with the url <a href="http://localhost:3000/users">http://localhost:3000/users</a>, we get the following result:</span><br><br></i><div style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_216gk2wjchn_b" style="height:121.755px;width:648px"></div><br><i><span style="font-style:normal">An xml output is similarly achieved by replacing json with xml in the method above. It almost feels like we are cheating here. We are writing very little code.<br><br>I will leave things here for now with respect to rendering content.<br><br>On my next post, I want to touch on the Testing<br><br></span></i><br><br><br><br><br><br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-1524596154694890252010-03-23T00:47:00.001-07:002010-05-12T06:38:24.275-07:00Rails - Day 8 - Down and Dirty with HAML and SASS<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In this post, I want to create a simple view and try and give justice to the power of <a href="http://haml-lang.com/" id="e9hu" title="HAML">HAML</a>, <a href="http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html" id="j2:l" title="SASS">SASS</a> and <a href="http://compass-style.org/" id="vt6k" title="Compass">Compass</a>. <br><br>In the previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="qclm" title="post">post</a>, we created a simple index action for our UsersController:<br><br><div id="snwf" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_214dq257cdf_b" style="height:160.38px;width:648px"><br>As you can see from the above, we are simply (in .NET terms) Response.Write'ing our output.<br><br>We will now add our first view. Guess what? View files are retrieved and the correct template handler (viewengine) is selected by using...........<i><b>a rails convention!!! <br></b><br><span style="font-style:normal">Shock, horror, whatever next? No xml configuration setting or line of code is needed. It is down to the file location and the file extension. In .NET convention over configuration rarely leaves the realms of assembly namespaces.</span><br><br><span style="font-style:normal">We have stated several hundred times now, how Rails promotes convention over configuration and the default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to actions. In our UsersController example, Rails will render a View if it is found in </span><span style="font-style:normal"><b>app/views/users/index.html.haml</b></span><span style="font-style:normal">. It just so happens in our example that we are rendering plain text as you can see in the index action at the top of the post.<br><br>It is important to know that the file extension of our view controls the choice of template handler. A template handler is analgous to the ASP.NET MVC ViewEngine. We are going to use HAML, so we add a file named<b> index.html.haml</b> to the location we mentioned above:<br><br></span></i><div id="ay.b" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_217dt9sq9hf_b" style="height:185px;width:186px"></div><br><h2>HAML</h2><div><font size="2">As mentioned in a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" id="ql53" title="post">post</a>, HAML uses a weird almost python like syntax to seriously reduce the amount of text that is entered into a view. HAML is most definitely not everyone's cup of tea and you will just have to judge for yourself if you would be more comfortable with something else like ERB. <br><br>So without further a do, let us see some HAML in the context of our sample project. By way of an entrée, I am just going to show you how the following part of the HTML document looks in HAML. Below is the HTML we want to create in HAML.<br></font><font size="2"><br></font><div id="vbsf" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_222f6dc99v5_b" style="height:216.481px;width:648px"><font size="2"><br>Here is the <b>index.html.haml</b> that this HTML was compiled from:<br><br></font><div id="w8xh" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_223cqnntjhm_b" style="height:179.577px;width:648px"></div><font size="2"><br></font></div><font size="2">I hope you can see from the above how the <b><meta></b> and <b><link></b> tags are identified as nested inside the <b><head></b> tag by using indentation. <br><br>Also, if we look at the meta tag syntax, we can see how HAML uses a hash to build up the attribute pairs:<br><br><b>%meta{'http-equiv' => 'Content-Type', :content => 'text/html; charset=utf-8'}</b></font></div><font size="2"><b><br></b></font><div><font size="2">becomes:<br><br></font><div><font size="2"><b><meta content='text/html; charset=utf-8' http-equiv='Content-Type' /></b><br><br>Also note how the % sign is start of a new element. If the HAML code makes you want to puke then you might want to look at ERB or something else. The lack of angle brackets is a big plus for me but not for everyone.<br></font><h2><font size="2"><b><b>Compass and SASS</b></b></font></h2></div></div><div style="text-align:auto">You can see from the HAML and the HTML above that we are pointing to the stylesheets that are compiled from the SASS files we talked about in this <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" id="jz_7" title="post">post</a>. <br><br>Compass is not a framework in the true sense of the word, it is put crudely, a lot of code you can use in your current project. In a previous <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" id="cbs4" title="post">post</a>, I mentioned how Compass created for us when we ran the compass installer, the following files in the <b>app/stylesheets</b> folder:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_180ctdd67f2_b"><br></div><br>These sass files compile down to the CSS we are referencing in our HAML <b>index.html.haml</b> file and are located here:<br><br><br><img src="http://docs.google.com/File?id=dgjpt2xx_183pqnqc8dn_b"><br><br>We never update the CSS directly and only make changes to the SASS which compiles down to the CSS.<br><br>I also mentioned the compass command line tool we can leave running that listens for changes in our sass files and compile into css automatically:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_182pvpwq36s_b"><br><br>The main SASS file is the <b>app/stylesheets/screen.sass</b> and here is mine, it should be noted that this has been altered from that which was created by the compass installer:<br><br><div id="u.ik" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_224gcvpf5dn_b" style="height:388.976px;width:648px"><br><br>What we have here at the top of the file are a number of @import statements which will include some shared libraries in the form of SASS mixins from various code files of the compass gem.<br><br>You can see that we are using the mixin semantics here by adding a mixin under the <b>#container</b> selector. A sass mixin allows us to reuse CSS attributes.<br><br>For example, I might declare the following mixin in my sass file:<br><br><div id="vasl" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_225fh35qqfx_b" style="height:231px;width:514px"></div><br>I can then reuse this mixin by using it in one or more selector like this where I am reusing it for a label class:<br><br><div id="m9qn" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_227dqt7vmgf_b" style="height:83px;width:270px"><br><br>Which will compile down to the following css:<br><br><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">01</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.label {</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">02</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-weight</font><font face="Courier New">: </font><font face="Courier New">bold</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">03</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-size</font><font face="Courier New">: </font><font face="Courier New">126%</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="'Courier New'">04</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">text-decoration</font><font face="Courier New">: </font><font face="Courier New">underline</font><font face="Courier New">}</font></td></tr></tbody></table></div><br>We are then free to reuse this mixin with other selectors. This is the power of SASS, the same reuse semantics just do not exist in CSS or perhaps SASS would not exist.<br><br>So the first thing we declare in our <b>screen.sass</b> file is the container selector and use the <b>+container</b> mixin that will give us the overall container css of the whole web page from the blueprint grid framework. <br><br>We declare the following SASS style rule:<br><br><div id="iay_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_315d82b4sdv_b" style="height:96px;width:279px"></div><br></div><div id="f8re" style="text-align:left"><br></div>If we look at the compiled version of the screen.css, we can see what the css has been compiled from the above SASS. <br><br><div id="e4oc" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_231hbrjwccw_b" style="height:50.7975px;width:648px"></div><div id="s5v:" style="text-align:left"><br></div><div id="tq0d" style="text-align:left">We can see that the container class style rule will set the width of the blueprint grid we are going to use on the page:<br></div><div id="b1fx" style="text-align:left"><br></div></div>The value of the width of the container selector is 950px, this size is set in the <b>app/stylesheets/partials/_base.sass</b> file:<br><br><div id="bude" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_232v4h2rxm8_b" style="height:248.229px;width:648px"><br><br>The sass variable used in the <b>+container</b> mixin is the statement <b>!blueprint_container_size = 950px<br></b><br></div><div id="oi01" style="text-align:left">OK, now we have created the container selector and added the container mixin, we can create the <b><body></b> tag and add a <b><div></b> tag to our index.html.haml file with an id of <b>container</b>.<br><br><div id="w47l" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_233drrbc8p8_b" style="height:84px;width:248px"><br><br>The <b>#container</b> HAML<b> </b>will render a div element like this <b><div id="container"></div> </b>and pick up the css style rules from the <b>#container</b> selector.</div><br></div>We are now going to add a header element to our page and add the sass to style the header. So first of all, we add the following to our <b>index.html.haml</b> file that simply renders an unordered list as an example of perhaps some top level navigation:<br><br><div id="m2w_" style="text-align:left"><div id="njes" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_237hjq2txff_b" style="height:396px;width:617px"></div><br><br>We then add the following sass to style the header element:<br><br><div id="m_ms" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_317gm3crcf6_b" style="height:448px;width:527px"></div><br><br>Here we are using the <b>+column</b> mixin that defines a selector as a column. The first argument is the number of grid units to span and there is also an optional second argument which is a boolean and indicates whether the column is the last of the current row. We are setting the number of columns to 24 which is the maximum amount of columns at our disposal as is stated in the <b>app/stylesheets/partials/_base.sass </b>file.<br><br>Here is how the resulting HTML looks in our browser:<br><br><div id="a_-1" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_239ghf8692j_b" style="height:39px;width:250px"></div><br><br>We are now going to create a sidebar which you can think of as a left menu element that a lot of web applications have. We add the following haml which will render a div with a child unordered list:<br><br><div id="kb45" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_240hrn2dg6p_b" style="height:406.843px;width:648px"></div>We then add the following sass to style the sidebar:<br><br><div id="swa4" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_241cpmsc9fz_b" style="height:103px;width:242px"></div><br>We only want the left menu to take up 5 columns worth of width, so we pass 5 into the column mixin. <br><br>We then want to add an element and some styling the main content of our page. We add the following HAML which is just a place holder for now:<br><br><div id="j:wy" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_242cbkxhnd4_b" style="height:101.731px;width:648px"><br><br>We add the following SASS to style the content:<br><br><div id="b735" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_327cjtzrqhb_b" style="height:151px;width:358px"></div><br></div><br>All that is left is to add the footer. We add the following footer HAML:<br><br><div id="xhdi" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_243cf2qk3hm_b" style="height:342px;width:607px"></div>The footer is remarkably like the header we early created. This is an ideal candidate for creating a mixin that we can reuse. With that decided, we create the following mixin declaration:<br><br><div id="dgna" style="text-align:left"><div id="ejn_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_247hhsgtbch_b" style="height:441px;width:467px"></div><br><br>I like to think of sass mixins as anonymous methods that we then add to our parent selectors.<br><br>We then refactor our header sass selector to look like this:<br><br><div id="x:-l" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_245hnbjd28b_b" style="height:143px;width:352px"></div><br>We can then easily create the footer sass selector easily as a carbon copy of the header:<br><br><div id="l2bp" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_318d9wb86fs_b" style="height:121px;width:340px"><br></div><br></div><div id="qw.z" style="text-align:left"><br>This was painlessly easy and the end result is cross browser CSS. The code in our SASS files are so much more clean and maintainable than the ugly sprawls that go under the name of CSS files. The HAML is bereft of inline styling and to me is much more elegant than the angle bracket soup you get in most other ViewEngines including the new ALT.NET darling view engine spark.<br><br>This is the basics of using SASS, HAML, Compass and the blueprint framework.<br><br>Before we leave the crazy world of Compass and his friends, I want to show the <b>+showgrid</b> mixin in action. <br><br>If we update the container selector to add the <b>+showgrid</b> mixin:<br><br><div id="z8x0" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_252g2jpdzn9_b" style="height:137px;width:242px"></div><br>This will give us a proper feel for the grid layout we are trying to achieve by setting the <b>+column </b>mixin and other values that move our elements left, right and up and down the page.<br><br>After adding the +<b>showgrid </b>mixin, our web page looks like this:<br><br><img src="http://docs.google.com/File?id=dgjpt2xx_250c4nfgnhj_b"><br><br>This is a great visual aid to deciding where to place your elements.<br><br></div></div><i><span style="font-style:normal"><span style="font-style:normal">We have covered a lot of ground and I will be surprised if anybody has actually made it this far and has not instead moved onto another blogger in your reader selection. <br><br>I am fairly sure that is what I would have done by now.<br></span></span><br><span style="font-style:normal">In the next post, I am going to touch on rendering content from a database which is really what a lot of us do. I include myself in this less than elite group.</span><br></i><br><br></div><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com2tag:blogger.com,1999:blog-5221465892382713395.post-70349361466726558082010-03-20T07:10:00.001-07:002010-03-20T07:18:47.953-07:00Nhibernate many-to-many with extra fields in the join tableThis blog post has been inspired by a twitter exchange I noticed at some point this week. <a href="http://twitter.com/jagregory" id="h_lz" title="@jagregory">@jagregory</a> tweeted that one of the most common questions on the FluentNhibernate forum was how to model a many-to-many relationship where the join table has extra fields. <br><br>I want to blog about the approach that I and I am sure countless others use.<br><br>You will have to forgive me for the archaic xml mapping files that I am going to use in this example. I have not made the jump to brave new world of the fluent interface of FluentNibernate. <br><br>In a typical <b>many-to-many</b> mapping, like the one below, the join table does not exist in the object world:<br><br><div><font color="#FF0000"><font size="4"><bag name="DistributionList" table="DistributionList" lazy="true" cascade="save-update" inverse="true"></font></font></div><div><font color="#FF0000"><font size="4"> <cache usage="read-write"/></font></font></div><div><font color="#FF0000"><font size="4"> <key column="planuid"/></font></font></div><div><font color="#FF0000"><font size="4"> <many-to-many class="ncontinuity2.core.domain.Contact,ncontinuity2.core" column="contactuid" not-found="exception"/></font></font></div><div><font color="#FF0000"><font size="4"></bag></font><br></font><br>But what if we have extra fields in the join table? <br><br>If for example, we have the following tables in our database schema:<br><br> <div id="ezyv" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_284gjfw6dgr_b" style="height:196.163px;width:648px"></div><br><br>You can see that the <b>UserPermissions</b> table contains extra fields apart from the two foreign keys of the linking tables. In the above example the <b>UserPermissions </b> join table has the extra fields of viewallowed and editallowed<br><br>Here is the mapping I use:<br><br><div><font color="#FF0000"><font size="4"><map name="UserPermissions" table="UserPermissions" lazy="true" cascade="none"></font></font></div><div><font color="#FF0000"><font size="4"> <cache usage="read-write"/></font></font></div><div><font color="#FF0000"><font size="4"> <key column="useruid" /></font></font></div><div><font color="#FF0000"><font size="4"> <index column="name" type="String" length="100"/></font></font></div><div><font color="#FF0000"><font size="4"> <composite-element class="ncontinuity2.core.domain.UserPermission,ncontinuity2.core"></font></font></div><div><font color="#FF0000"><font size="4"> <parent name="User"/></font></font></div><div><font color="#FF0000"><font size="4"> <property name="ViewAllowed" column="viewallowed" type="Boolean" /></font></font></div><div><font color="#FF0000"><font size="4"> <property name="EditAllowed" column="editallowed" type="Boolean" /></font></font></div><div><font color="#FF0000"><font size="4"> <many-to-one name="Permission" class="ncontinuity2.core.domain.Permission,ncontinuity2.core" column="permissionid" cascade="all"/></font></font></div><div><font color="#FF0000"><font size="4"> </composite-element></font></font></div><div><font color="#FF0000"><font size="4"></map></font></font></div><br>The above mapping is from my user.hbm.xml file that models the user as having a dictionary of composite elements which map to a class named <b>UserPermission</b>. <br><br>Here is how this looks in a class diagram:<br><br><div id="l.34" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_2834smwxnhf_b" style="height:447.946px;width:648px"></div><br>The following should be noted from the above diagram:<br><br><ol><li>The <b>User </b>object contains a dictionary of <b>UserPermission </b>ojects.<br></li><li>The <b>UserPermission</b> object contains both the <b>Permission</b> object, which is the other side of the <b>man-to-many</b> relationship <b>and</b> the extra fields of the join table.</li></ol><br></div><div>Hopefully this gives you another option to achieve the desired result.</div><br><div>I am not saying I am right, I am just saying how I do it.</div><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com1tag:blogger.com,1999:blog-5221465892382713395.post-73875408946211769062010-03-18T01:54:00.001-07:002010-05-12T06:37:45.260-07:00Rails - Day 7 - Our first View<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" id="rmwe" title="post">post</a>, I introduced the <a href="http://blueprintcss.org/" id="u51l" title="blueprint CSS framework">blueprint CSS framework</a>, <a href="http://haml-lang.com/docs.html" id="fmd_" title="HAML">HAML</a>, <a href="http://sass-lang.com/" id="jzrf" title="SASS">SASS</a> and last but by no means least <a href="http://github.com/chriseppstein/compass/tree" id="n7pe" title="Compass">Compass</a>.<br><br>Compass allows us to harness the power of a CSS framework like blueprint with the programmability of Sass and HAML. In this post, I want to show how to set compass to compile your sass files into css and also fire up a simple view.<br><br>I have got to the point now with Compass, were I really do not understand how I ever did CSS without Compass and sass. I also do not think I would have stayed with Blueprint without Compass.<br><br>Now without further a do, let us install all the bits into our rails application.<h2>Requirements</h2><ul><li>HAML/Sass Gem </li><li>Compass Gem </li><li>Configure Rails for Haml </li><li>Configure Rails for Compass</li></ul><br>Thankfully those very smart people from compass have come up with a rails installer that we can use that will get the bits for us and give us a wizard like experience to configure our rails app.<br><br>If we were creating a new rails project from scratch, we would create it with the <b>-m</b> switch pointing to the compass installer.<br><br><b>rails datacapturer -m http://compass-style.org/rails/installer<br></b><br>As we have already created our project, we are going to issue the following command to install compass:<br><br><b>rake rails:template LOCATION=http://compass-style.org/rails/installer</b><br><br>The compass installer will then ask us the question:<br><br><b>What CSS Framework do you want to use with Compass? (default: 'blueprint')<br></b><br><div id="u6q-" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_1754kjk2vcg_b" style="height:224px;width:648px"></div><br>We can just press enter to keep the defaults. We will then be asked to confirm:<br><br><b>Where would you like to keep your sass files within your project?<br></b><br><div id="ukm_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_176cmwnx7dz_b" style="height:102px;width:648px"></div><br><b>Where would you like Compass to store your compiled css files? (default: 'public/stylesheets/compiled')<br></b><div>We can just press enter to keep the defaults, so we just press enter. We will then be asked to confirm:<br><br></div><div id="y9y7" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_177gzpgccct_b" style="height:44px;width:648px"><br><br>We can just press enter to keep the defaults. We will then be asked to confirm:</div><br>The installer will then install the requirements listed above and execute the various commands to configure both haml and compass for our application:<br><br><div id="y_zs" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_178g3jt79gk_b" style="height:203px;width:648px"></div><br>I have done the steps that the pre-installer performs for us by hand and although not overly complex, the installer makes it all the more easier.<br><br>As the installer says, we are ready to have fun.<br><br>So what has the installer done, if we look at our rails project directory and in the app folder, we can see the following files have been added:<br><br><div id="ul5y" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_180ctdd67f2_b" style="height:430px;width:300px"></div><br><br>Three sass files have been added, ie.sass, print.sass, screen.sass and to the app/stylesheets folder and a _base.sass file has been added to the <b>app/stylesheets/partials</b> folder.<br><br>You will also see that under the <b>public/stylesheets</b> directory, an empty directory has been created called <b>compiled</b>:<br><br><div id="wmwa" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_181fcmkpgc5_b" style="height:211px;width:300px"></div><br>Sass compiles down to regular (ugly) CSS. <br><br>So how do we get the sass to start compiling down to css?<br><br>Thankfully compass comes with a command line tool. One of the options of the command line tool is the <b>--watch or -w </b>switch. When the compass command line tool is invoked with this switch, the tool will monitor the current project for changes in the sass files and will then compile the sass down into css. So without further a do, let us start Compass monitoring, we cd into our project directory and issue the<b> compass -w </b>command which will generate the following response from compass:<br><br><div id="z_0f" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_182pvpwq36s_b" style="height:126.396px;width:648px"></div><br>You can see from the above that it has created 3 files that match our sass files in the <b>public/stylesheets</b> folder, <b>ie.css, print.css </b>and<b> screen.css:<br></b><br><div id="uvwo" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_183pqnqc8dn_b" style="height:253px;width:250px"><br><br>Any changes to the sass files will result in the CSS being compiled. A quick look at the screen.css file reveals that the sass has generated a whole raft of css for us.<br><br>I think it is high time I created a view to hopefully start bringing some of this together.<br><br>First of all, we want to at least check that we can bring up the default rails template web page. In order to do this, we need to boot up our development web server. WEBrick is the rails equivalent of the Web Developer Server that we use with ASP.NET named cassini. Like Cassini, we access it via localhost on a high-numbered port that doesn't interfere with other services on our machine. WEBrick starts on port 3000 by default. We start the web server with the <b>ruby</b> <b>script/server command:<br></b><br><div id="o5yy" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_185hg7p7jcb_b" style="height:160.704px;width:648px"></div><br>if we type the following url into our browser, http://localhost:3000/, we are greeted with the following page<br><br><div id="l0pw" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_186hsg566dh_b" style="height:459px;width:514px"></div><br><br></div>If you remember for <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" id="rmro" title="part 4">part 4</a>, one of the files that was generated for us by the <b>script/generate resource users </b>command was the <b>users_controller.rb</b> file which is of course our users controller class. I am hoping that most people now understand the concept of MVC by now but if not, MVC uses a particular magic called routing to route our http requests to methods on our controller classes. Think of routing as a type of URL rewriting that will break down the url and extract the correct controller class and then invoke the correct class method.<br><br>If you have ever written an HttpModule or such like in order to achieve Url rewriting, you can breath easy. We merely configure the routes. All the other plumbing is done for us.<br><br>I now want to show that the <b>script/generate resource users </b>command has updated the routes.rb file correctly. I want to test out the index action of the UserController is working. In order to do this, I add a method named index to my UsersController class:<br><br><div id="yz_0" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_1873fn5gdd8_b" style="height:150px;width:596px"></div><br>As you can see, we simply want to output some text on the page. Typing in the url <b>http://localhost:3000/users</b>, gives us the following result in our browser:<br><br><div id="k7ev" style="text-align:left"><div id="x6xa" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_192whmj98g4_b" style="height:171px;width:377px"></div>This brings us on nicely to how exactly the url http://localhost:300/users is routed or mapped to the index method of our UsersController class. </div><h2>Rails and REST</h2><p>I mentioned in an earlier post about the resource generator. Rails uses the word resource in the context of a REST resource. The resources are the nouns of your application, an example of such a noun in our application is the User resource we have generated for this example. If the resources are the nouns, then the verbs are the HTTP verbs:<br><br></p><ul><li><font size="2"><b>GET </b>is used for safe operations like Read</font></li><li><font size="2"><b>POST </b>is used for unsafe operations like Create.</font></li><li><font size="2"><b>PUT </b>is used to create or update a resource at a specific URL. It changes or creates a resource.</font></li><li><font size="2"><b>DELETE </b>is the most obviously named verb as it deletes a resource at a specified URL.<br></font></li></ul><br>I am personally not a huge fan of REST. The above form a minimal set and it is only in more recent times that browser vendors have implemented PUT and DELETE. It is also worth noting that HTML or at least pre HTML 5 does not support PUT or DELETE either. So in order for rails to conform to the REST standard, it uses a hidden form field to simulate PUT and DELETE requests. Thankfully you do not have to conform to REST practices 100% in rails but to try and fight it would be very much swimming against the tide. On the plus side, REST gives you a consistent interface to program against.<br><br>If we can put my cynicism to one side, you can see in the table below how rails has taken the four HTTP verbs described above and maps them to seven controller actions and they will appear on our UserController. Actions in this context are the public methods of your controller:<br><br><table border="1" bordercolor="#000000" cellpadding="3" cellspacing="0" id="hyhe"><tbody><tr><td align="center"><b>Rails Action</b></td><td><b>Description</b></td><td align="left"><b>URL</b></td><td align="center"><b>HTTP Verb</b></td></tr><tr><td align="center">index</td><td>A collection of Users</td><td align="left">/users</td><td align="center">GET</td></tr><tr><td align="center">show</td><td>A User</td><td align="left">/users/123</td><td align="center">GET</td></tr><tr><td align="center">new</td><td>A form for creating a new User </td><td align="left">/users/new</td><td align="center">GET</td></tr><tr><td align="center">create</td><td>Where you submit the new User form </td><td align="left">/users</td><td align="center">POST</td></tr><tr><td align="center">edit</td><td>A form for editing an existing User</td><td align="left">/users/123/edit </td><td align="center">GET</td></tr><tr><td align="center">update</td><td>Where you submit the edit User form</td><td align="left">/users/123</td><td align="center">PUT</td></tr><tr><td align="center">destroy</td><td>Where destroy (delete an existing User)</td><td align="left">/users/123</td><td align="center">DELETE</td></tr></tbody></table> <br>Notice how the URLs for the show, update and destroy are identical but use different HTTP verbs.<br><br>You can see below the line that the script/resource generator has added to our <b>routes.rb</b> file that will hook up these 7 out of the box methods:<br><br><div id="jrf4" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_197cm5wfpcq_b" style="height:63.2847px;width:648px"></div><br><h2>Custom Routing</h2>We are of course not confined to these 7 actions in our controllers but in a way, it is nice to find this level of consistency to have the same 7 actions that do the same seven things on each controller. <br><br>We can of course add our own by updating the routing file. Let us say we wanted to add an action that would display all our users that were enabled in our application. This is another good example of the workflow of rails. We currently do not have an enabled field on our user object so we can create a new migration with the following command:<br><br><div id="rv6-" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_198c9r9zqc6_b" style="height:107.576px;width:648px"></div><br>We then update the newly created migration file 20100310051724_add_enabled_to_user.rb to the following<br><br><div id="c210" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_203gqk3qpc9_b" style="height:261.032px;width:648px"></div><br><br>We then run rake to update our schema:<br><br><div id="n9pt" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_199cvwt2wcs_b" style="height:177.691px;width:648px"></div><br>We can then update our seeds.rb file with the following code that will now, delete all users in the database and then update any seed data with the new enabled flag:<br><br><div id="r63e" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_202g2xwsvhm_b" style="height:152.554px;width:648px"></div><br>OK, now let us add a show_enabled action to our controller:<br><br><div id="ri0d" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_213czchddck_b" style="height:263.119px;width:648px"></div><br><br>The observant of you might be asking where the <b>find_all_by_enabled </b>method has come from but I will leave that as an exercise for you the reader. Needless to say, we did not write it.<br><br>Now, if I type the url <a href="http://localhost:3000/users/show_enabled" style="color:#551a8b">http://localhost:3000/users/show_enabled</a> into my browser, I get the following response:<br><br><div id="lqtj" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_206d88r86f8_b" style="height:168px;width:450px"></div><br>So how do we map this show_enabled_action in <b>routes.rb</b>?<br><br>In rails it is possible to add what are known as <b>member </b>routes such as:<br><br><div id="e5mx" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_207f3kbkggc_b" style="height:70.9185px;width:648px"></div><br>This will enable rails to recognise URLs such as <b>/users/1/preview/</b> using the GET HTTP verb or as in our <b>show_enabled </b>example action, we can use a <b>collection </b>route that looks like the following:<br><br><div id="j:li" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_208f8mgqqfv_b" style="height:89.7231px;width:648px"></div><br>Hopefully this has shed some light on how the routing in rails works. <br><br>In the next post, I want to get back onto the power of HAML, SASS and Compass.<br><br><br><br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-80277896655475277862010-03-17T03:04:00.001-07:002010-05-12T06:37:10.411-07:00Rails - Day 6 - Blueprint, Compass, SASS and HAML<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" id="st_0" title="post">post</a> I talked about rails migrations and how schema modifications are made through the rails workflow and how rails takes care of the lowlevel SQL.<br><br>After the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" id="enpu" title="post">post</a>, we have in theory seeded our ever so simple database thanks to a combination of rake and rails. <br><br>We are now going to move onto some UI with an introductory piece on a few of the tools we are going to use to create our views.<br><br>I am going to introduce a few exciting things I have discovered in Rails which you might be able to use in a greenfield .NET project. I am going to introduce them and then bring them together with a trivial example. The first owes nothing to rails and is a new craze that is sweeping the design landscape and is that of the CSS Grid Framework<br><h2><b>CSS Grid Frameworks</b></h2>Layout grids are an old concept used in print publishing long before the Web. They are essentially a lattice that divides horizontal and vertical space in consistent units where, text, headlines, images and just about any visible HTML element can be placed. <br><br>Below is an example of a grid layout and how we might begin to think about our web pages in a printing style that pre-dates the web. We start to think about anchoring UI elements to specific coordinates of a grid:<br><br><div id="jf_s" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_17255fxt7hk_b" style="height:400px;width:396px"><br><br>As I hope you can see on the image above, we carve up our content into regions and then place html elements within these regions. I will explain later how we can position our elements at these specific coordinates but it is a thrilling concept for a CSS heretic like myself. CSS to me has, until recently, been a matter of thrashing and guess work for most of the time. When I first started to use a CSS framework, it was a complete revelation that I could actually precisely position my html elements anywhere on a page. <br><br>After some diligent research, it seemed that <a href="http://blueprintcss.org/" id="cl6." title="blueprint">blueprint</a> was the most feature rich and active framework out there. Feature rich by my estimation anyway.<br><br>I had been looking into CSS frameworks about the same time as I started getting into rails and I rightly assumed that rails would have grasped this concept and come up with its own unique slant on how to harness this emerging technology.<br><br>A little research turned up Chris <a href="http://github.com/chriseppstein/compass/tree" id="pq.p" title="Eppstein's Compass gem">Eppstein's Compass gem</a>. This is a very nice meta-framework, which can be used with rails and comes in a nicely packaged gem and also has blueprint support. I excitedly progressed further.<br><br>It turned out that compass uses something I had never heard of before named <a href="http://sass-lang.com/" id="bzpa" title="Sass">Sass</a> to provide the power of frameworks like BluePrint. Sass it seems is rarely mentioned in the same breath without the view engine HAML.<br><br>I will now offer a brief introduction to HAML, SASS and Compass.<br><h2><b>HAML and SASS</b></h2><div style="text-align:auto">These two tools take the verbosity out of HTML and CSS. <br><br><b>HAML - XHTML Abstraction Markup Language</b> uses a variation of the convention over configuration to substantially reduce the amount of text in a view, by using indentation to demarcate blocks. And since div is the most commonly used element, you don't need to type it at all except when there's no ID or class assigned to it. A simple example is</div><br><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">01</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">%h1 This is the title</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">02</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">03</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.some_class Some free text</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">04</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">05</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.someother_class</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">06</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.userclass</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">07</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.username.label Name</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">08</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.userage.label Age</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">09</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">10</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">- </font><font face="Courier New">for</font> <font face="Courier New">user </font><font face="Courier New">in</font> <font face="Courier New">users</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">11</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.username= user.username</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">12</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.userage= user.age</font></td></tr></tbody></table></div><br><p><font size="3">The above generates the following HTML:<br><br></font></p><div class="syntaxhighlighter" id="highlighter_937441" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="lines" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">01</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New"><</font><font face="Courier New">h1</font><font face="Courier New">>This is the title</</font><font face="Courier New">h1</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">02</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">03</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"some_class"</font><font face="Courier New">>Some free text</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">04</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"someother_class"</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">05</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"userclass"</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">06</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"username label"</font><font face="Courier New">>Name</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">07</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"userage label"</font><font face="Courier New">>Age</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">08</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"username"</font><font face="Courier New">>Bill</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">09</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"userage"</font><font face="Courier New">>35</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">10</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"username"</font><font face="Courier New">>Viv</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">11</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"userage"</font><font face="Courier New">>32</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">12</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"username"</font><font face="Courier New">>Chris</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">13</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"><</font><font face="Courier New">div</font> <font face="Courier New">class</font><font face="Courier New">=</font><font face="Courier New">"userage"</font><font face="Courier New">>28</</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">14</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New"></</font><font face="Courier New">div</font><font face="Courier New">></font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">15</font></td></tr></tbody></table></div></div></div><br><b>SASS - Syntactically Awesome StyleSheets</b> offers similar simplification for CSS and adds the use of multiple types of variables, directly as constants and through mixins. Mixins enable you to define reusable groups of CSS attributes and then include them inline. As in HAML, indentation is used to represent content and inclusion.<br><br>By content I mean attributes and the corresponding value and inclusion that a selector is effective only when used as a child of the previously specified selector:<br><br><div class="syntaxhighlighter" id="highlighter_137317" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="lines" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">01</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">!heavy = bold // a simple variable</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">02</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">=heavy_label // a mixin</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">03</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">:font</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">04</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">:weight</font> <font face="Courier New">!heavy</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">05</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">:size</font> <font face="Courier New">126</font><font face="Courier New">%</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">06</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">:text</font><font face="Courier New">-decoration underline</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">07</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">08</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">.some_class</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">09</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">+heavy_label</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">10</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">:color</font> <font face="Courier New">#0000bb</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">11</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">12</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">.label</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">13</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">+heavy_label</font></td></tr></tbody></table></div></div></div><p><font size="3"><br>This SASS gets this CSS:<br><br></font></p><div class="syntaxhighlighter" id="highlighter_489648" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="lines" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">01</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"><font face="Courier New">.some_class {</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">02</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-weight</font><font face="Courier New">: </font><font face="Courier New">bold</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">03</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-size</font><font face="Courier New">: </font><font face="Courier New">126%</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">04</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">text-decoration</font><font face="Courier New">: </font><font face="Courier New">underline</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">05</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">color</font><font face="Courier New">: </font><font face="Courier New">#0000bb</font><font face="Courier New">;}</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">06</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> </td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">07</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">.label {</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">08</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-weight</font><font face="Courier New">: </font><font face="Courier New">bold</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt1 line" style="background-color:#ffffff;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">09</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">font-size</font><font face="Courier New">: </font><font face="Courier New">126%</font><font face="Courier New">;</font></td></tr></tbody></table></div><div class="alt2 line" style="background-color:#f8f8f8;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><table class="zeroBorder" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><tbody style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline"><tr style="background-color:initial;background-image:none;border-color:initial;border-style:initial;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:baseline;width:auto"><td class="number" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#afafaf;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:3em"><font face="Courier New">10</font></td><td class="content" style="background-color:initial;background-image:none;border-color:initial;border-style:initial;color:#000000;float:none;font-style:normal;height:auto;margin-left:0px;margin-right:0px;text-align:left;vertical-align:top;width:auto"> <font face="Courier New">text-decoration</font><font face="Courier New">: </font><font face="Courier New">underline</font><font face="Courier New">}</font></td></tr></tbody></table></div></div></div></div><h2>Compass</h2>Compass takes the CSS supplied Blueprint and other supported frameworks and transforms them into SASS files; HAML comes with utilities to Convert SASS to CSS, HAML to HTML and vice versa. There are also some other command line utilities, all invoked through the compass executable. For rails projects, compass automatically updates the compiled CSS files whenever you change the SASS.<br><br>In this post we have introduced the blueprint CSS framework, HAML, SASS and last but by no means least Compass.<br><br>In the next post we will create our first view.<br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com4tag:blogger.com,1999:blog-5221465892382713395.post-89765146665988363372010-03-16T03:44:00.001-07:002010-05-12T06:36:01.589-07:00Rails - Day 5 - Rails Migrations<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" id="zr38" title="post">post</a> I introduced the concept of generators and how they help the workflow of the rails developer.<br><br>Also in the last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" id="mrz6" title="post">post</a>, I mentioned that one of the files that rails generated for us was the rather cryptically named <b>20100307123141_create_users.rb which </b>which can be found in the <b>db/migrate</b> folder.<br><br><div id="e9qr" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_161g7hrt2gd_b" style="height:188.372px;width:648px"></div><br><br><div>The observant amongst you will have noticed that this is a time stamped file with our resource (user) included in the name. The name of the file is important as it is yet another Rails convention. As our application increases, so will the number of migrations. The timestamp tells Rails the order to run the files. <br><br>So what is this crazy concept of migrations all about? <br><br>Below is a look at the above file<br><br><div id="rq3f" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_211gjfwfbc2_b" style="height:430.847px;width:648px"></div><br><br>A migration is a ruby script that could be classified as a DSL. Rails will take care of all the lowlevel SQL for you. This is all done platform agnostic. We can in theory switch from MySQL to SQL server with ease. I must add the caveat that I have never done this.<br><br>You can see from the above that there are two methods to the migration, <b>up</b> and <b>down</b>. The up method tells Rails what to do when migrating up and the down tells rails what to do when rolling back.<br><br>It is worth noting that the script generator has only generated the files. The database migration does not take place until we run the file. It is now time to introduce another hero in this rails story which you may well have heard of. I speak of the one they call <i><b>RAKE.</b><br><br><span style="font-style:normal">Rake is Ruby's automation and task-running utility. Rake is what first pricked my interest in Ruby on Rails. After years of fighting with nant's horrendous Xml syntax, moving to Rake proved to be a breath of fresh air. If you are currently using Rake to build your .NET projects then you might possibly be unaware of just how integral rake is to the workflow of a Rails project. I am going to devote another post to Rake, so I will just leave it at that. You might not be totally surprised that when you generate a rails project, you get a rake file in the root of the project that is ready for use. We are again being prompted to have a build script. I have watched developers copy .dlls directly from the bin folders and do manual changes to the web.config before releasing to production. Shame on you!! One of your first tasks in any project is to have a way of building everything.</span><br><br><span style="font-style:normal">To see what commands are available to us for rake that are migration related, we can simply type the following into the command prompt </span><span style="font-style:normal"><b>rake -T db:migrate </b></span><span style="font-style:normal">which will display something similar to the following:</span><br><br></i><div id="m5l1" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_164g42shbdn_b" style="height:133.173px;width:648px"></div><br>We can see from the above that we have rake tasks that correspond to <b>up</b> and <b>down</b> methods that are methods in our migration script.<br><br>Let us run our first migration. I enter the command <b>rake db:migrate</b> and get the following confirmation:<br><br><div id="v:3t" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_165fn33cfc7_b" style="height:114.545px;width:648px"></div><br>So a table has been created, but where? We are in the land of abstractions do not forget. Remember that we are executing these migrations on the development environment and unless we tell rails otherwise, Rails will use the SQLite3 engine for our development db and test environments. If we open our db folder, we can see there is a new development.sqlite3 file.<br><br>OK great, we have created a table which is all well and good but what is the workflow if we want to add or remove columns for our table. What we don't want to do is to start messing about with the database scheama ourselves. We want to stay within migrations. This way our migrations are available to other developers who can run the scripts and we also have a complete upgrade and rollback plan if needs be. Staying with the scenario of our example, we are going to add a <b>date_of_birth </b>date field to our user.<br><br>Our first step in making the required change to the schema is to use a rails generator named unsurprisingly migration to generate us a migration stub. We simply issue the command <b>ruby script/generate migration add_date_of_birth_to_users</b> which will result in the following confirmation:<br><br><div id="rmih" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_166hr2fj2cn_b" style="height:83.3258px;width:648px"></div><br>If we look at our <b>db/migrate</b> folder, we can see the generated file:<br><br><div id="qga5" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_167fz5gpn29_b" style="height:189px;width:500px"></div><div id="toq1" style="text-align:left"><br></div><div id="x:pu" style="text-align:left">We now need to add the following code to the self.up method of the newly created migration and the code for the down method of the newly created migration:<br><br><div id="l.80" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_212c3kckmgp_b" style="height:236.2px;width:648px"></div><br></div><br>We now need to run the migration with the same command as we used last time, namely rake <b>db:migrate</b>. This results in the following confirmation:<br><br><div id="o.b0" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_169gdb82jcr_b" style="height:111.78px;width:648px"><br><br><h2>Seeding</h2></div>Before we leave the topic of rails migrations which is one well worth digging deeper into, I want to bring up the convention Rails uses for seeding or initialising your database with test or look up data.<br><br>If we look in the db folder of our rails application you will notice that by default rails has added a seeds.rb file:<br><br><div id="bke_" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_170cd7kg3gx_b" style="height:250px;width:625px"></div><br></div>It is this file which will contain our initial data. There is also a rake task named <b>rake db:seed </b>that will execute the command. Guess what, this is another rails convention that I find really exciting. Maybe I should get out more but this says to me how serious rails is about conventions. It even has a convention for seed data. <br><br>To add some test users to the users table, I add the following code:<br><br><b>User.create!(:user_name => "admin", :password => "password", :full_name => "Paul Cowan", :emailaddress => "dagda1@scotalt.net", :date_of_birth => Date.new(1970, 12, 5))</b><br><br><div>If I issue the command <b>rake db:seed </b>then the above code in the <b>seeds.rb</b> file will be ran. Nothing exciting here but a nice convention none the less.<br><br>And that is all I have to say about migrations for now. As usual the underpinnings of migrations are down to rails conventions.</div><h2>The Rails Console</h2>Another great utility I find exciting that is created for you when you create a rails project, is the rails console. It is a lot like the interactive shell irb in that it lets you issue commands with immediate response. The difference with the rails console is that it is running in the context of your rails web application. This means that, in addition to evaluating ruby interactively, you can do things like manipulate your application's database.In order to start the rails console, we cd into our application directory and issue the command <b>ruby script/console</b>:<br><br><div id="p0h-" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_193d25s25d4_b" style="height:161px;width:635px"></div><br>As we have just created and seeded our virgin database, we can check that the data is where we think it is by running some ruby through the command prompt. We are going to touch on the Ruby ORM ActiveRecord later.<br>In order to retrieve all the users that are are currently in the users table of our db, we are input the following ruby text User.all which will list all the records in the users table of our SQLLite3 development database. You can think of the all method as a static method on the User class that will return all records in the user table and load each record into a user object for us:<br><div id="ldtz" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_194ftmd5xc5_b" style="height:119.07px;width:648px"></div><br>Obviously, the exciting thing here is that we are running these commands in the context of our rails application. This is a great scratch pad to test and query.<br><h2>The Rails Logs</h2>Another great tool that you get out of the box is logging already configured and ready to go. If you look in your project folder, you will see a ~/log folder that contains log files for all our application environments:<br><div id="g28i" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_195ftfg48f8_b" style="height:250px;width:274px"></div><br>If we want to examine what SQL was generated from our ActiveRecord statements like the User.all statement we just issued, we can simply look in the specific environment log in question, in our case development log. Below, you can see the actual SQL issued:<br><br><div id="pmap" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_196f7vmtxhd_b" style="height:39.0817px;width:648px"></div>If you are familiar with Nhibernate, you can contrast this with having to configure your application in order to read the sql statements, you need to set up log4net and set the <b>show_sql = 'true' </b>etc.<br><br>In Rails, everything is already configured and logging. This is something I find very frustrating when I return to .NET land. Nothing is configured and everything is left to you, the beleaguered user.<br><br>In this post, we have introduced migrations, the rails application console and logging.<br><br>In my next post, I want to move onto views in rails.<br><br><br><br><br><br><br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0tag:blogger.com,1999:blog-5221465892382713395.post-77495135708418676162010-03-15T07:15:00.003-07:002010-05-12T06:36:32.029-07:00Rails - Day 4 - Rails Generators<a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-love-part-1-rubygem-love-story.html" title="Rails Love - Part 1 - The RubyGem Love Story">Rails - Day 1 - The RubyGem Love Story</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-2-power-of-consistency.html" title="Rails - Day 2 - The Power of Consistency">Rails - Day 2 - The Power of Consistency</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" title="Rails - Day 3 - Rails environments">Rails - Day 3 - Rails environments</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-4-rails-generators.html" title="Rails - Day 4 - Rails Generators"><font color="#800080">Rails - Day 4 - Rails Generators</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-5-rails-migrations.html" title="Rails - Day 5 - Rails Migrations"><font color="#800080">Rails - Day 5 - Rails Migrations</font></a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-6-blueprint-compass-sass-and.html" title="Rails - Day 6 - Blueprint, Compass, SASS and HAML"><font color="#800080">Rails - Day 6 - Blueprint, Compass, SASS and HAML</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-7-our-first-view.html" title="Rails - Day 7 - Our first View"><font color="#800080">Rails - Day 7 - Our first View</font></a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-8-down-and-dirty-with-haml.html" title="Rails - Day 8 - Down and Dirty with HAML and SASS">Rails - Day 8 - Down and Dirty with HAML and SASS</a><br><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-9-rendering-content.html" title="Rails - Day 9 - Rendering Content">Rails - Day 9 - Rendering Content</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-10-testing-rails-controller.html" title="Rails - Day 10 - Testing Rails Controllers">Rails - Day 10 - Testing Rails Controllers</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-11-model-testing-and.html" title="Rails - Day 11 - Model Testing and 101 ActiveRecord">Rails - Day 11 - Model Testing and 101 ActiveRecor</a></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-12-activerecord-relationships.html" title="Rails - Day 12 -ActiveRecord Relationships Part I">Rails - Day 12 -ActiveRecord Relationships Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-13-activerecord-relationships.html" title="Rails - Day 13 -ActiveRecord Relationships Part II">Rails - Day 13 -ActiveRecord Relationships Part II</a><br><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-14-rails-forms-part-i.html" title="Rails - Day 14 - Rails Forms Part I">Rails - Day 14 - Rails Forms Part I</a></div><a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-15-rails-forms-part-ii.html" title="Rails - Day 15 - Rails Forms Part II">Rails - Day 15 - Rails Forms Part II</a><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-16-validation.html" title="Rails - Day 16 - Validation">Rails - Day 16 - Validation</a><br></div><div><a href="http://thesoftwaresimpleton.blogspot.com/2010/04/rails-day-17-update-and-delete.html" title="Rails - Day 17 - Update and Delete">Rails - Day 17 - Update and Delete</a></div><br>In my last <a href="http://thesoftwaresimpleton.blogspot.com/2010/03/rails-day-3-rails-environments.html" id="ples" title="post">post</a>, I illustrated how a guaranteed folder structure allows a lot of magic in Rails to flow. This magic is called convention over configuration and allows us to free ourselves from the shackles of external xml configuration files.<br><br>Generators in rails are probably the first thing that really got me salivating for more rails. Generators are what made me stand back and think, hold on a second, these guys might just be onto something here.<br><br>In .NET the workflow commands are executed via the IDE, generally in the form of the Add File menu option. Let us break down that previous statement. I am more often that not adding a single file as a .NET workflow command. This statement is in itself limiting. The only way I can generally add a group files is in the form of a Visual Studio project. Not very exciting or more importantly not very productive. <br><br>There are and have been many code generators in many platforms that I have used and a couple, I have wrote myself but as I am sure you have experienced, they very rarely cut it. A Rails generator is as you might have guessed, is a unit of such code generation. So why is this better than the various approaches that I have just lambasted?<br><br>The rails generators are better because they are not trying to generate a whole project from a database or anything big time like that. They are an important part of the workflow that allows Rails to push and prod you into doing the right thing or going down the right path. It is these little nuggets of ingenuity that make it difficult for the developer to do the wrong thing. This is yet another example of Rails exerting its pushy and if I did not know better, I would assume is a somewhat <b><i>Irish</i></b> personality.<br><br>Another feature of rails I want to illustrate with this post on generators is the often overlooked help available in rails. The request for help is issued through the command line and not via the search box of the google home page, so this again is a switch of thinking. Let us see what help is available on generators. We simply type <b>ruby</b> <b>script/generate </b>and after a fairly lengthy wait that I find hard figuring out why, we are greeted with the following text:<br><br><div id="u7:s" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_155fd66xrd2_b" style="height:584px;width:648px"></div><br>This help text shows us all of the options and generators that are currently available. We are going to concentrate on the <b>resource</b> generator for this post but a good exercise for the reader is to check out the other generators on this list. Certainly cucumber seems to be one of the current ALT.NET darlings that I am less convinced about but you can generate a lot of your cucumber boilerplate code from here.<br><br>Here is a list of generators available from the rubyonrails <a href="http://wiki.rubyonrails.org/rails/pages/availablegenerators" id="n55x" title="wiki">wiki</a>.<br><br>Not only do we get help on generators as a whole but we can also further drill down and get help on specific generators. As previously mentioned, we are going to look at the <b>resource</b> generator. To get help on this, we simply type <b>ruby script/generate resource</b>. We are then presented with the following:<br><br><div id="d0-s" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_157crtc28j8_b" style="height:771px;width:648px"></div><br>As you can see from the above, there is a more than adequate explanation and contains the usual list of switches and options available.<br><br>What the above text is basically saying is that rails is going to create all the files necessary for us to work with what we are stating here is a resource. Rails is using <b>resource </b>in the context of a <b>REST </b> resource. You can think of resources as the nouns in your application. Typical examples are User, Customer, Order, Role etc. <br><br>Resources are an abstraction we are going to perform operations against. In strict REST terms, these actions are CRUD actions, Create, Read, Update and Delete. I am not a REST zealot or RESTifarian or whatever the hell they are called, so the last sentence might be incorrect. <br><br>The resource we are going to create for our simple, semi-mythical application is a User.<br><br><div>In order to generate our resource cruft , we issue the following command<b> ruby script/gnerate resource User user_name:string, password:string, full_name:string</b>. It should be noted that I am adding some initial fields to the User resource that will be added to our model after the <b> ruby script/gnerate resource User </b>statement. We get the following confirmation screen of what rails has generated:<br><br><div id="msqk" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_158xf746jdz_b" style="height:385px;width:648px"></div><br>So let us look at what Rails has created for us.<br><br><ol><li>A new helpers module in <b>app/helpers/users_helpers.rb</b>. This again is great because all our helpers will be consistently named and consistently located. </li><li>A new class definition of our user model class in <b>app/models/user.rb</b>. </li><li>A new controller in <b>app/controllers/users_controller.rb</b>. </li><li>A unit test has been created for us in <b>test/unit/user_test.rb</b>. The message from Rails is clear, <b>write tests now! </b>Even if we are not currently doing TDD, our concience is being prodded by the creation of this file. A helper class in <b>test/unit/helpers/users_helper_test.rb </b>has also kindly been added if we need a helper for our tests.<br><br></li></ol><div id="ajoq" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_209fx83b3c2_b" style="height:421px;width:400px"></div><br><br><div>This generation of consistently named files makes me feel good and clean about the world in general.<br><br>Rails has also updated our <b>config/routes.rb</b> file to add an entry for users.<br><br><div id="ck8j" style="text-align:left"><img src="http://docs.google.com/File?id=dgjpt2xx_210ccp4khgg_b" style="height:85px;width:648px"></div><br></div><div>I hope you can see the contrast in productivity between the Add File paradigm of Visual Studio and the resource generation of a Rails application.<br></div><br>It is also worth noting that the resource generator does not create any views for us. It does create a folder in <b>app/views/users </b>but it does not create a view. There is a scaffold generator that will do that for you but I am not a huge fan of scaffolding.<br><br>Let us be perfectly clear here, there is nothing a generator can do that you cannot do yourself. They just take the tedium out of some common Rails tasks that you'd otherwise do by hand.<br></div><br>It is worth noting that what we can generate, we can also equally destroy. There is a <b>script/destroy </b>script that will so the dirty deed for us.<br><br>Another file that the generator script has generated for us will form the subject of my next post. It is cryptically named <b>db/migrate/20100307123141_create_users.r</b>b.<br><br>This brings us onto the concept of Rails migrations. Stay tuned for our next thrilling instalment that will explain the concept.<br><br><br><br><br><br><br><br><br><br><br><br><br>Paul Cowanhttp://www.blogger.com/profile/11492473992478172640noreply@blogger.com0