<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5724099066106449846</id><updated>2012-02-16T23:11:59.260-05:00</updated><title type='text'>Madking's Musings</title><subtitle type='html'>A forum to discuss thoughts, experiences, problems, complaints, questions, and findings in the field of software engineering.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>87</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-7319986764687083485</id><published>2011-10-31T09:00:00.000-04:00</published><updated>2011-10-31T09:00:20.033-04:00</updated><title type='text'>Chasing Technology</title><content type='html'>Maybe I'm just getting old, but keeping up with technology seems to be getting harder and harder. As I've mentioned before, I am a big fan of &lt;a href="http://madkingsmusings.blogspot.com/2011/09/learn-your-libraries.html"&gt;learning your libraries&lt;/a&gt;, languages, and tools. But how are you supposed to do that, when the libraries keep upgrading and there are constantly new tools to use? Just this past week there was a post on slashdot suggesting that &lt;a href="http://developers.slashdot.org/story/11/10/26/1748201/your-tech-skills-have-a-two-year-half-life"&gt;tech skills have a 2 year half life&lt;/a&gt;. The comments on that thread seem to mostly disagree with the hypothesis, but there is definitely some truth to it.&lt;br /&gt;&lt;br /&gt;The two languages that I use the most at work did not exist when I started college. However, languages are easy to keep up with. They tend to move slowly and are very well documented. Tools, libraries, and frameworks, on the other hand, are a nightmare to chase. They are constantly being upgraded, and often times the best documentation are the question/answers out there on the web at places like &lt;a href="http://stackoverflow.com/"&gt;stackoverflow&lt;/a&gt;. The problem with these sites is that they are only useful after a tool has been out long enough for a critical mass of people to use.&lt;br /&gt;&lt;br /&gt;So what's a person supposed to do?&lt;br /&gt;&lt;br /&gt;Well, first, as many commenters from the &lt;a href="http://developers.slashdot.org/story/11/10/26/1748201/your-tech-skills-have-a-two-year-half-life"&gt;slashdot article&lt;/a&gt;&amp;nbsp;stated, the most valuable skills a software engineer has is knowing how to design, develop, and debug software. These skills don't go away just because you are using a new language or library. So play to your strengths. If you design and write clean code, it'll be easier to fix/update as you learn the language/library/tool better.&lt;br /&gt;&lt;br /&gt;Second, choose your upgrades carefully and intentionally. Just because there is a newer version of something out there, doesn't mean you need to use it. Are the benefits worth it to upgrade now? Does it make more sense to wait until it has been popularly adopted? Or maybe it makes sense to skip a version or two.&lt;br /&gt;&lt;br /&gt;Third, don't become an expert in everything. Most of us have finite amounts of energy and time. So, while I'm a big believer in being an expert in the tools you are using, that isn't always practical. If a particular tool/library/framework is bound to be upgraded/replaced in the near future, it's probably ok if you use the existing library in a less than optimal way. Focus your energies on the timeless (design and coding skills) or the long lasting (languages).&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;constantly learn&lt;/span&gt;Fourth, and most importantly, constantly learn. If your employer doesn't provide opportunities for professional growth, take it anyway. Just as you don't ask for permission for every single test that you write, or every whiteboard design that you draw, you don't need permission to explore new technologies and tools. Whether they know it or not, your employer pays you to be a good and competent software developer. This requires you to be constantly learning new and better ways of doing things. If you are not taking ten to twenty percent of your time exploring and learning, you are shortchanging yourself and making yourself obsolete. I realize that it seems there is never time to do this, but like other aspects of good software engineering (appropriate design, testing, documentation), in the long run you will be worse off if you don't make the investment now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-7319986764687083485?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/7319986764687083485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=7319986764687083485' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7319986764687083485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7319986764687083485'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/10/chasing-technology.html' title='Chasing Technology'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-7942010557584859112</id><published>2011-10-10T09:00:00.000-04:00</published><updated>2011-10-10T09:00:03.663-04:00</updated><title type='text'>Vexing Bugs</title><content type='html'>While bugs are a part of development, there are a few types of bugs that I find particularly vexing: intermittent bugs, library bugs, and bugs that only happen in production.&lt;br /&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Intermittent Bugs&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: arial; font-size: small;"&gt;It's annoying to perform the exact same set of steps 4 times, have it work 3 of the times, and fail once. When &lt;a href="http://en.wikipedia.org/wiki/Murphy's_law"&gt;Murphy&lt;/a&gt;&amp;nbsp;has his way, it works when you, perform the steps or are watching, but fails when the user does it on their own. How do you diagnose a bug that you can not reliably reproduce?&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Library Bugs&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: arial; font-size: small;"&gt;The majority of library bugs aren't actually bugs in the library, but rather a bug in how you are using the library. Either way, having cryptic error messages coming out of the bowels of a library, when there is no apparent connection between the error and your code, is not fun. I find the process of googling error messages or library usages to be much more frustrating than tracking down errors that are entirely in my own source code.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial;"&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Production Bugs&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;While we strive to have our development environment similar to the production environment, there are certain&amp;nbsp;discrepancies&amp;nbsp;that always creep in. Things like debug information, optimization level, database source, etc. When in development, you don't want to be messing with production data, and your willing to give up some performance to better track the code. But since it isn't the same, code that works fine in development doesn't always work in production. Ugh.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;This Week&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;So what prompted this post? Well, this week I had a bug that was at the intersection of these bugs. We had deployed a new version of our application, and suddenly I started getting &lt;a href="http://madkingsmusings.blogspot.com/2011/08/emailing-server-errors.html"&gt;server emails about errors&lt;/a&gt;. It was some cryptic error happening within the library we use for making Web Service calls. And, of course, the errors only happened sometimes. Even more frustrating, when trying to reproduce the error on my local box using &lt;a href="http://en.wikipedia.org/wiki/WEBrick"&gt;WEBrick&lt;/a&gt;&amp;nbsp;in development mode, the error &lt;b&gt;never&lt;/b&gt;&amp;nbsp;happened.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;After setting up a development server on my local box that was configured just like the production box, I was able to intermittently reproduce the error. Using my standard debugging technique of &lt;a href="http://stackoverflow.com/questions/189562/what-is-the-proper-name-for-doing-debugging-by-adding-print-statements"&gt;adding printfs&lt;/a&gt;, I eventually noticed output that looked like:&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Starting Web Service Call 1&lt;br /&gt;Starting Web Service Call 2&lt;br /&gt;Exception Caught from Web Service Call 2&lt;br /&gt;Ending Web Service Call 1&lt;/span&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;Unfortunately, it didn't catch my eye right away, but I eventually noticed that every time there was an error, the web service calls were getting interleaved. i.e. Web Service Call 2 was starting before Web Service Call 1 started. And now it suddenly made sense.&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;The two web service calls were being made as the result of two separate AJAX callbacks from a web page. Since the the two web service calls were being done in two different web requests, they were being handled in parallel resulting in a race condition.&amp;nbsp;As it turns out, the web service library we were using is not thread safe. However,&amp;nbsp;apparently WEBrick in development mode was serializing these requests (i.e. not processing 2 until 1 was completely handled), and so in development there were no threading issues.&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;As with most bugs, once the problem had been diagnosed, it wasn't that hard to fix. In this case, we put the web service calls in a &lt;a href="http://en.wikipedia.org/wiki/Critical_section"&gt;critical section&lt;/a&gt;, forcing them to be serialized. For our particular use case, the higher latency of serializing the web service calls was acceptable, allowing for a fairly simple solution.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Moral&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;This post wasn't really intended to have a moral. It was mostly just me talking about my week, but I suppose there are a couple good ideas.&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;ul&gt;&lt;li&gt;If you think its a library bug, you are probably misusing the library. (i.e.&amp;nbsp;&lt;a href="http://madkingsmusings.blogspot.com/2011/09/learn-your-libraries.html"&gt;Learn your libraries&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Intermittent errors are&amp;nbsp;indicative&amp;nbsp;of threading issues.&lt;/li&gt;&lt;li&gt;Know the differences between your development and production environments.&lt;br /&gt;oh, and maybe most importantly of all&lt;/li&gt;&lt;li&gt;When frustrated, &lt;a href="http://madkingsmusings.blogspot.com/2007/03/dont-get-stuck-in-rut.html"&gt;take a break&lt;/a&gt;. I didn't actually track down the bug until I walked away from it for a while and then came back to it with fresh eyes.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div style="font-family: arial; font-size: small;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-7942010557584859112?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/7942010557584859112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=7942010557584859112' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7942010557584859112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7942010557584859112'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/10/vexing-bugs.html' title='Vexing Bugs'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-2318278112168954597</id><published>2011-09-26T09:00:00.000-04:00</published><updated>2011-09-26T10:20:53.115-04:00</updated><title type='text'>Learn Your Libraries</title><content type='html'>&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Story 1&lt;/span&gt;&lt;br /&gt;I still remember the month long final project from my first ever CS class. Actually, I remember almost nothing from the project except for a bug that caused me to have to throw away my first three weeks of work.&amp;nbsp;I had just learned about enumerations in Pascal and thought they were the neatest thing since sliced bread. I designed my entire project around enumerations. Users would enter their menu choices via enumerations. I would display the enumerations as options. All of this depended very heavily on my understanding of enumerations which, as it turned out, was in no way related to what enumerations actually are.&lt;br /&gt;&lt;br /&gt;Unfortunately, I didn't find this out until I had spent two weeks writing the entire program and tried running it. It didn't work at all. Like any good developer I randomly changed various lines of code until it worked. A week later, with the project deadline looming, my project was not any closer to working. It was time for drastic action. Since I couldn't seem to get the enumerations to do what they were "supposed" to do, I actually read up on them. Imagine my dismay when I realized that all the assumptions that I had made were wrong. And not just a little bit wrong. The reality (enums are a typesafe way to have named constants) and my vision (they'll do everything I want, including cooking dinner and solving the halting problem) were completely unrelated. I spent the rest of the day griping and complaining about why would anybody create such a useless design feature. When I realized that that wasn't getting me any closer to a program that I could actually turn in, I finally sat down and rewrote the entire program using only Pascal features that I actually understood, and eschewed enumerations entirely.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Story 2&lt;/span&gt;&lt;br /&gt;The other day a friend asked me what I knew about &lt;a href="http://oreilly.com/catalog/javarmi/chapter/ch10.html"&gt;Java Serialization&lt;/a&gt;. After asking for details, it turns out that he was working on a project where they were doing &lt;a href="http://graphics.stanford.edu/%7Eseander/bithacks.html"&gt;bit-diddling&lt;/a&gt;&amp;nbsp;on the Serialized output of an object. One of the assumptions that they had was that if you had an objects instance1 and instance2, and you serialize both of them, that the serialized byte data would be identical if instance1 and instance2's fields are all identical. They had run a bunch of experiments and it seemed like their assumption wasn't always holding. I think the question to me was hoping that I would point out something they had missed that would give them an easy fix.&lt;br /&gt;&lt;br /&gt;Upon check the &lt;a href="https://www.google.com/#sclient=psy-ab&amp;amp;hl=en&amp;amp;source=hp&amp;amp;q=serialization+spec&amp;amp;pbx=1&amp;amp;oq=serialization+spec&amp;amp;aq=f&amp;amp;aqi=g2g-v2&amp;amp;aql=&amp;amp;gs_sm=e&amp;amp;gs_upl=51819l54723l11l54933l18l9l10l2l4l0l224l339l0.1.1l2l0&amp;amp;bav=on.2,or.r_gc.r_pw.r_cp.&amp;amp;fp=c6b95d63df18470d&amp;amp;biw=1052&amp;amp;bih=593"&gt;Serialization Spec&lt;/a&gt;, it turns out their assumption was not valid.&amp;nbsp;My suggested solution was to not use Java's Serialization, but rather write their own conversion to bytes that would fit their specific needs. This may seem like reinventing the wheel, but as &lt;a href="http://madkingsmusings.blogspot.com/2010/08/dont-reuse-rewrite.html"&gt;I've mentioned before&lt;/a&gt;, I believe that rolling your own is often the way to go.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Story 3&lt;/span&gt;&lt;br /&gt;On a project I once worked on, the decision was made that all data was going to be handled as XML. All of the data was stored as an XML document in a single column in a database. To process individual data items, &lt;a href="http://www.w3schools.com/xpath/xpath_syntax.asp"&gt;XPath&lt;/a&gt;&amp;nbsp;queries would be made. HTML reports would be created by running giant &lt;a href="http://www.w3schools.com/xsl/xsl_transformation.asp"&gt;XSLT&lt;/a&gt; transforms on the data. The assumption was that because XML has such great support, it would be easy to add new data (just turn it into XML and merge it with the other data), and create new reports (just write a new XSLT).&lt;br /&gt;&lt;br /&gt;The reality is that just because you can create a general tool to handle syntactically correct XML, you still need to customize the logic for the semantics. Which meant every new data source added still required code and logic changes. As for generating the HTML via XSLT transformations, its very difficult to use modern HTML features, like Ajax, this way. And by making such a large portion of the code base XSLT and XPath, we couldn't take advantage of the IDE's ability to do easy code completion, navigation, and refactoring, or leverage the development team's OO skill to create small, easily testable, and reusable components. And then there's the performance issues, both in time and space, that were encountered with trying to manipulate and store large quantities of large XML documents.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Moral&lt;/span&gt;&lt;br /&gt;The moral of these stories is that you need to know your tools. Enumerations, Serialization, and XML are all great, when used appropriately. When not... well, invalid assumptions lead to bugs. Invalid assumptions about crucial components of your software lead to large scale rewrites. To avoid the large scale rewrite, we often have a period of denial where we try to make the unworkable work. The worst case is that with enough bubble gum, chicken wire, and ingenuity we do make it work, at least some of the time. This results in a maintenance nightmare keeping it working and large scale rewrites are even less likely to happen once you've got "working" code.&lt;br /&gt;&lt;br /&gt;While we all know that assumptions can be bad, the danger is when we don't realize we are making assumptions. In the first story I thought I know what enumerations did. The developers in the second story had used Java Serialization on many projects and thought they fully understood it.&amp;nbsp;In the third story the benefits of XML were considered without awareness of all of the limitations and restrictions that came along.&amp;nbsp;i.e. We didn't know what we didn't know.&lt;br /&gt;&lt;br /&gt;The conservative solution to this problem is to only use technologies you have already used successfully on all your projects. While this seems to be favored by many people, I am too much a fan of &lt;a href="http://www.passionforbusiness.com/articles/shiny-object-syndrome.htm"&gt;shiny objects&lt;/a&gt;&amp;nbsp;and so like to try new things. The key to using new technologies (or existing ones in new ways) is to limit the scope until you have used it successfully. If it is going to be integral to your design, create a small throw-away prototype first to make sure you find the dark corners and sharp edges first.&lt;br /&gt;&lt;br /&gt;The most important solution, though, is to be willing to admit you were wrong. Its impossible to get very far in life without making assumptions, and despite your best precautions, sooner or later, you will be wrong about an assumption. Rather than try to patch on hack after hack to try to make a bad assumption viable, be willing to break from the past and rewrite/redesign code given your new knowledge. Even if it means throwing away a lot of work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-2318278112168954597?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/2318278112168954597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=2318278112168954597' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2318278112168954597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2318278112168954597'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/09/learn-your-libraries.html' title='Learn Your Libraries'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6356503544045092452</id><published>2011-09-19T09:00:00.000-04:00</published><updated>2011-09-19T09:00:19.074-04:00</updated><title type='text'>RSpec Lessons Learned</title><content type='html'>Previously I showed some unit tests written in &lt;a href="https://www.relishapp.com/rspec"&gt;RSpec&lt;/a&gt;. Those test were fairly ugly, partially because I didn't really understand RSpec. In an effort to have better unit tests, I have learned a little more about RSpec.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Read the Latest Documentation&lt;/span&gt;&lt;br /&gt;You'd think this would be obvious, but given the lack of links to the documentation on the various "here's advice on using RSpec" blogs, it must not be done a lot. Worse, somehow I had been only looking at the &lt;a href="http://rspec.info/"&gt;old documentation&lt;/a&gt;&amp;nbsp;for version 1 of RSpec. So, if you are going to use RSpec, I suggest you read latest documentation, which as of when I am writing this is &lt;a href="https://www.relishapp.com/rspec"&gt;RSpec&amp;nbsp;2.6&lt;/a&gt;. This will definitely be helpful.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Make Tests Self Documenting&lt;/span&gt;&lt;br /&gt;Each test (it or specify block) can take a description. Sometimes this is necessary, but any time you have documentation (and that's what this description is), you risk having the documentation be out of sync with the code. While &lt;a href="http://blog.carbonfive.com/2010/10/21/rspec-best-practices/"&gt;not everyone agrees&lt;/a&gt;, I am a fan of letting the tests document themselves, whenever possible.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Make Use of Context&lt;/span&gt;&lt;br /&gt;Use the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;context&lt;/span&gt;" keyword to describe what you are doing in a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;before&lt;/span&gt;" block to set up the state necessary for testing.&lt;br /&gt;&lt;div class="p1"&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; context &lt;span style="color: #00c4c4;"&gt;"empty object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #9999a9;"&gt;#insert tests&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; context &lt;span style="color: #00c4c4;"&gt;"with inherited object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:base&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; base&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;prototype &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; base&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #9999a9;"&gt;#insert tests&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Make Use of Describe&lt;/span&gt;&lt;br /&gt;Use the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;describe&lt;/span&gt;" keyword to describe either the noun that is being tested, or the actions that are under test. i.e. if you have actions in a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;before&lt;/span&gt;" block that &lt;b&gt;are&lt;/b&gt;&amp;nbsp;the actions being tested, you should you use "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;describe&lt;/span&gt;" rather than "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;context&lt;/span&gt;"&lt;br /&gt;&lt;div class="p1"&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; context &lt;span style="color: #00c4c4;"&gt;"empty object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"when assigning via properties"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #9999a9;"&gt;# insert checks&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Make Use of Subject&lt;/span&gt;&lt;br /&gt;If you have multiple tests on a single object, make it the RSpec Subject and put it in a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;describe&lt;/span&gt;s block. This way, all of your "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;should&lt;/span&gt;" comparisons will be implicitly on this object.&lt;br /&gt;&lt;div class="p1"&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; context &lt;span style="color: #00c4c4;"&gt;"empty object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_empty&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Use Its&lt;/span&gt;&lt;br /&gt;Often times you want to test the properties of an object. You can use the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;its&lt;/span&gt;" method to have the implicit subject of "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;should&lt;/span&gt;" comparisons be the result of the method or array dereference specified.&lt;br /&gt;&lt;div class="p1"&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; context &lt;span style="color: #00c4c4;"&gt;"empty object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'newProperty'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'newMethod'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Let and Subject are Lazy Loaded&lt;/span&gt;&lt;br /&gt;The "variables" defined in "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;let&lt;/span&gt;" calls and the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;subject&lt;/span&gt;" aren't actually evaluated until they are used. So if you never reference a variable specified in a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;let&lt;/span&gt;", then that code is never executed. This also means that order isn't important. &amp;nbsp;i.e. the following will work:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;let &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:a&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;b &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;let &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:b&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;specify&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"show using let"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;a&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;b&lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Shared_examples and shared_contexts are Global&lt;/span&gt;&lt;br /&gt;I actually haven't found this documented, so I may be doing something wrong. But I have found that if I have two different rspec files that each have a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;shared_examples_for 'test this object'&lt;/span&gt;", this causes problems. I can test each rspec file in isolation and it is fine. But if I try to test both at the same time, I get an error saying that a shared example already exists with the name "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;test this object&lt;/span&gt;".&lt;br /&gt;&lt;br /&gt;There are two different solutions to this problem that I have found. If the shared examples are the same, pull them into a common &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Module&lt;/span&gt; that is included. Note, this is better than having repeated code anyway. If the examples are different, then you have to be more unique with the names of the shared examples.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Final Thoughts&lt;/span&gt;&lt;br /&gt;It seems that the approach of RSpec is to write tests such that they are self descriptive and so that each test tests exactly one thing. While in theory, this sounds good, I am finding that this results in very verbose test files. That's even with the cleaning up that I have done after learning RSpec better. &amp;nbsp;i.e. I like a lot of what RSpec does, but I am not convinced that it is the best way to write unit tests.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Rewritten Tests&lt;/span&gt;&lt;br /&gt;Using what I have learned, I have rewritten the unit tests &lt;a href="http://madkingsmusings.blogspot.com/2011/09/unit-testing-simple-property-model.html"&gt;from before&lt;/a&gt;. Here is what they look like now:&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;require&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;'spec_helper'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;  let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;  subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  shared_examples_for &lt;span style="color: #00c4c4;"&gt;"simple object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |map, self_keys|&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should have&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;items&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"self_keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; self_keys ||&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should have&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;items&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"fields"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |k, v|&lt;br /&gt;        its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;k&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; v&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;      its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'noSuchProperty'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"methods"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |k, v|&lt;br /&gt;        its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;k&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; v&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;      its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'noSuchMethod'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"to_hash"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should have&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;items&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;map&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should include&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;map&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  context &lt;span style="color: #00c4c4;"&gt;"empty object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_empty&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'newProperty'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    its&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'newMethod'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"to_hash"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;should have&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;items&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"when assigning via properties"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;      it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple object"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"when assigning via methods"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bar &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;      it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple object"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  context &lt;span style="color: #00c4c4;"&gt;"with inherited object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;    let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:base&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      base&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;      base&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;      thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;prototype &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; base&lt;br /&gt;      thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;      thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;    it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple object"&lt;/span&gt;, &lt;br /&gt;      &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;15&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;, &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;    describe &lt;span style="color: #00c4c4;"&gt;"when overriding values"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;25&lt;/span&gt;&lt;br /&gt;        thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hola"&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;      it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple object"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;25&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hola"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;15&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Below is what the output looks like. As you can see, if you read it, it describes the tests that are being run more clearly than the old version of the tests.&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;$ rspec spec&lt;span style="color: #d2cd86;"&gt;/&lt;/span&gt;models&lt;span style="color: #d2cd86;"&gt;/&lt;/span&gt;thing_spec&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;rb &lt;br /&gt;&lt;br /&gt;Thing&lt;br /&gt;  empty object&lt;br /&gt;    keys&lt;br /&gt;      should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;      should be empty&lt;br /&gt;    &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"newProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;      should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;    newMethod&lt;br /&gt;      should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;    to_hash&lt;br /&gt;      should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;      should have &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; items&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;when&lt;/span&gt; assigning via properties&lt;br /&gt;      it should behave like simple object&lt;br /&gt;        keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        self_keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        fields&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        methods&lt;br /&gt;          foo&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;          bar&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;          noSuchMethod&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        to_hash&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should include &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;when&lt;/span&gt; assigning via methods&lt;br /&gt;      it should behave like simple object&lt;br /&gt;        keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        self_keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        fields&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        methods&lt;br /&gt;          foo&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;          bar&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;          noSuchMethod&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        to_hash&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;br /&gt;          should include &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;  with inherited object&lt;br /&gt;    it should behave like simple object&lt;br /&gt;      keys&lt;br /&gt;        should have &lt;span style="color: #008c00;"&gt;4&lt;/span&gt; items&lt;br /&gt;        should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;, &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;        should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;      self_keys&lt;br /&gt;        should have &lt;span style="color: #008c00;"&gt;2&lt;/span&gt; items&lt;br /&gt;        should include &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;        should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;      fields&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;          should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;      methods&lt;br /&gt;        foo&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;        bar&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;        baz&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;        bye&lt;br /&gt;          should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;        noSuchMethod&lt;br /&gt;          should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;      to_hash&lt;br /&gt;        should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        should have &lt;span style="color: #008c00;"&gt;4&lt;/span&gt; items&lt;br /&gt;        should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;, &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;        should include &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;15&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;when&lt;/span&gt; overriding values&lt;br /&gt;      it should behave like simple object&lt;br /&gt;        keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;4&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;, &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        self_keys&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;4&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;, &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; include &lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;br /&gt;        fields&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;25&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hola"&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"noSuchProperty"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        methods&lt;br /&gt;          foo&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;25&lt;/span&gt;&lt;br /&gt;          bar&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hola"&lt;/span&gt;&lt;br /&gt;          baz&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;          bye&lt;br /&gt;            should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;          noSuchMethod&lt;br /&gt;            should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;        to_hash&lt;br /&gt;          should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;          should have &lt;span style="color: #008c00;"&gt;4&lt;/span&gt; items&lt;br /&gt;          should include &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;, &lt;span style="color: #e66170; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;          should include &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;25&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"hola"&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;15&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Finished &lt;span style="color: #e66170; font-weight: bold;"&gt;in&lt;/span&gt; &lt;span style="color: #009f00;"&gt;0.0831&lt;/span&gt; seconds&lt;br /&gt;&lt;span style="color: #008c00;"&gt;78&lt;/span&gt; examples, &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; failures&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6356503544045092452?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6356503544045092452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6356503544045092452' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6356503544045092452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6356503544045092452'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/09/rspec-lessons-learned.html' title='RSpec Lessons Learned'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-1978643142275619253</id><published>2011-09-12T09:00:00.001-04:00</published><updated>2011-09-12T09:00:08.553-04:00</updated><title type='text'>Birth of a Bug</title><content type='html'>This is a a story of a bug. It was a simple bug. Like many simple problems, on the surface it had a simple cause. Also like many simple problems, in reality it had many contributing factors. Before I tell you about the bug, let me tell you about the application.&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;let people configure which emails they get&lt;/span&gt;At my place of employment we have various electronic forms that need to be signed by people. For example, if you want to purchase something, there are people who need to approve that purchase - probably your supervisor plus whoever has signature authority for the project you are billing the purchase to. To keep the process flowing people get emails letting them know when they have a form to sign. As it turns out, not everyone wants these emails, and sometimes people only want the emails under certain circumstances. So I wrote an app to let people configure which emails they get.&lt;br /&gt;&lt;br /&gt;I deployed this app the other day after work, and promptly the next morning people started using it to turn off email notifications. Shortly thereafter they started sending in complaints that they were still receiving email.&lt;br /&gt;&lt;br /&gt;Why didn't it work? How did such a fundamental bug get into the application?&lt;br /&gt;&lt;br /&gt;Before answering that question, I'll tell you what the bug was. One of the features of the configuration is that people could turn off email notification, but add a financial exception. As an example, some people only want to get email notifications for requests that are for more than $10,000. Somewhere in the user's &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;NotificationPreferences&lt;/span&gt; object is a line that looks similar to:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mMoney&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;where &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;amount&lt;/span&gt; is the amount the form is for ($53.24 stapler), and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mMoney&lt;/span&gt; is the threshold the user set ($10,000). There's a catch though - what is &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mMoney&lt;/span&gt; if the user didn't specify a threshold? Turns out that there are two possible values, depending on how the value got set. If they never have set any preferences at all, this value is &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;NULL&lt;/span&gt;, but if they have set a preference for configuration, but never set a threshold, this value gets set to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;0&lt;/span&gt;. This means that we do NOT want to send a financial override if the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mMoney&lt;/span&gt; value is either &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;NULL&lt;/span&gt; or &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;0&lt;/span&gt;. The actual code looked like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mMoney&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Don't bother looking for the bug in that code, as the bug isn't there. At least not yet. It didn't show up until deployment day.&amp;nbsp;While the bug was simple, there were a number of "best practices" that were violated that led to the birth of this bug.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Don't Make Last Minute Changes&lt;/span&gt;&lt;br /&gt;The plan was to deploy the code at 5PM. At 3PM, I had a coworker who suggested that "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;amount&lt;/span&gt;" was a better name for the database column for the threshold than "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;money&lt;/span&gt;" because "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;amount&lt;/span&gt;" is consistent with other projects. (and, of course, "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mAmount&lt;/span&gt;" would be the Java variable containing the threshold value.)&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Anytime you make a change, the risk of breaking something is non-zero. Make the change at the last minute and you won't have time to exhaustively test it. As you have figured out, we broke the code. Then in my "smoke testing" I only chose examples that didn't trigger the new bug. Which is how I unknowingly release code with this bug.&lt;br /&gt;&lt;br /&gt;This wasn't a critical user bug. Heck, this wasn't anything that was going to affect the user in any way, shape, or form.&amp;nbsp;I never should've agreed to make this change.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Beware of Semantic Code Changes&lt;/span&gt;&lt;br /&gt;When making this last minute name change from "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;money&lt;/span&gt;" to "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;amount&lt;/span&gt;" we split the work up between me and the developer who recommended the change, even though all the code involving this had been written by me. This means that he was changing code that he wasn't familiar with. Here is what the updated code looked like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; mAmount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mAmount&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As you can see, the condition for checking if "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mAmount &amp;gt; 0&lt;/span&gt;" got dropped. Why was it dropped? I haven't asked, so I don't know. I don't know if it was accidental or intentional. Either way, there are related best practices that should have been followed.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Understand Your Changes&lt;/span&gt;&lt;br /&gt;Ever run across code in a working system that just can't be right? Before just assuming that the bug is being hidden by other functionality and going ahead and fixing the code, make sure you understand exactly what is happening. If you have access to the original developer (in this case I was just across the hall, a slightly raised voice away), ask why the code is the way it is. It might be a bug, but you might also not be understanding it.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Leverage Refactoring Tools&lt;/span&gt;&lt;br /&gt;If you are just renaming a variable, let your IDE rename it for you. This is the type of change that can almost always be done automatically by tools. Use those tools.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Check Your Changes Before Committing&lt;/span&gt;&lt;br /&gt;This is something I am a big enough believer in that I may turn it into its own blog post at some point. After making code changes, no matter how large or how trivial, run diff on the code before checking it in. Verify that every change that you are committing is one that you mean. This serves as a simple and quick code review, as well as ensuring that you don't accidentally commit left in debugging statements. Not to mention catching those places where you accidentally inserted or deleted code because of careless clicks.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Run Your Unit Tests&lt;/span&gt;&lt;br /&gt;After making any changes, no matter how trivial, make sure you rerun your unit tests. Especially before deploying. You do have unit tests, don't you? Ok, I'll admit it, we had no unit tests on this project, and this is exactly the type of bug that would've gotten caught by unit tests.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Write Understandable Code&lt;/span&gt;&lt;br /&gt;While you can follow all the best practices under the sun, you can't force your coworkers to do the same. The best thing you can do to defend against that is to write code that is not only correct but understandable and obvious. Let's look at that query one more time:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mMoney&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Checking for null is fairly obvious as it prevents &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;NullPointerExceptions&lt;/span&gt; when the &lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/autoboxing.html"&gt;autounboxing&lt;/a&gt;&amp;nbsp;happens. The comparison "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;amount &amp;gt;= mMoney&lt;/span&gt;" is the comparison that is needed. But what's with the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mMoney &amp;gt; 0&lt;/span&gt;"? If I hadn't explained it above, would you have guessed why it is here? Maybe, maybe not, either way I would argue that this is a piece of non-obvious code. I will offer up two possible suggestions for how this code could be made more obvious. The first is to add a comment:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #9999a9;"&gt;// if mMoney &amp;lt;= 0, it is not a valid value and should be ignored&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mMoney&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;But since I think comments are often a sign of confusing code, my other suggestion involves changing the code:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;boolean checkFinancialOverride(Double amount) &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; isThresholdValid&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; amount &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; mMoney&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;boolean isThresholdValid() &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;!&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;null&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;amp;&lt;/span&gt; mMoney &lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;Write clean code. Don't make last minute changes. Understand your changes. Write unit tests and use them. These are the steps to avoid unexpected and avoidable bugs.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-1978643142275619253?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/1978643142275619253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=1978643142275619253' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1978643142275619253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1978643142275619253'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/09/birth-of-bug.html' title='Birth of a Bug'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-380537270718108865</id><published>2011-09-05T09:00:00.009-04:00</published><updated>2011-09-05T09:00:11.125-04:00</updated><title type='text'>Unit Testing a Simple Property Model</title><content type='html'>Previously I showed the beginning of a &lt;a href="http://madkingsmusings.blogspot.com/2011/08/simple-property-model-in-ruby.html"&gt;Ruby implementation of a simple property model&lt;/a&gt;.&amp;nbsp;Well, I decided to create some unit tests to help verify that my property model correctly handles properties, especially with the prototype delegation.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt;&amp;nbsp;seems to be popular in the rails world, so I used rspec to write my tests. This exercise provided me with examples of the &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-good.html"&gt;good&lt;/a&gt;, the &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-bad.html"&gt;bad&lt;/a&gt;, and the &lt;a href="http://madkingsmusings.blogspot.com/2011/02/unit-testing-ugly.html"&gt;ugly&lt;/a&gt;&amp;nbsp;with unit tests. The good was straightforward. I don't have an application yet, just this simple library and unit tests provided an easy way to test it. Even better, at one point I changed an implementation detail that required some rewriting of the property model, and by rerunning the tests, I had confidence that I didn't break anything.&lt;br /&gt;&lt;br /&gt;The ugly? This is definitely a good example of ugly. I have the unit testing code at the bottom of this post. I found I had a lot of duplication, so I created "&lt;a href="http://rspec.info/documentation/"&gt;shared_examples_for&lt;/a&gt;" to reduce redundancy. However, the resulting test code is still really long and while the shared examples helps with sharing tests, I am not sure they help with readability. Now, as &lt;a href="http://madkingsmusings.blogspot.com/2011/02/unit-testing-ugly.html?showComment=1300759434884#c5639012959821131617"&gt;Trevor commented&lt;/a&gt;, its possible (ok, likely) that the unit tests are ugly because they weren't written well. I will admit to being a newbie at RSpec. I am sure that they could be written better, but I am not convinced they wouldn't still be somewhat ugly.&lt;br /&gt;&lt;br /&gt;The bad? Well, there isn't too much bad, yet. It did take time to create the tests, but I had to validate my code somehow. And it is possibly (likely?) that my tests aren't as thorough as they should be, so maybe I have some false confidence in the correctness of my code. But all in all, despite what I said last paragraph, this wasn't that good of an example of the bad. At least not yet. The bad will come when I spend time trying to clean up these tests, rather than using that time to write new code.&lt;br /&gt;&lt;br /&gt;And without further ado, here are these unit tests.&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;require&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;'spec_helper'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;describe Thing &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:foo&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:bar&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; shared_examples_for &lt;span style="color: #00c4c4;"&gt;"simple properties"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; foo&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; foo&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; bar&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bar&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; bar&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;baz&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"to_hash"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; foo&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; bar&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; shared_examples_for &lt;span style="color: #00c4c4;"&gt;"nested properties"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'b2'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"foo"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"bar"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bar&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"b2"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'b2'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;b2&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bye&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"baz"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;baz&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"to_hash"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'b2'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"no inheritance"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"new object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"keys"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_empty&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"properties"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"to_hash"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should_not be_nil&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"adding properties to object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"adding fields to object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bar &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"inheritance"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:thing&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; base &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; base&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; base&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;'hello'&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; foo &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;prototype &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; base&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; subject &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"new object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"adding properties to object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'b2'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bye'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"nested properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"adding fields to object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;b2 &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bye &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"nested properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; describe &lt;span style="color: #00c4c4;"&gt;"adding redundant fields to object"&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:foo&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; let&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:bar&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"bar2"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; before&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;:&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;foo &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;bar &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it_should_behave_like &lt;span style="color: #00c4c4;"&gt;"simple properties"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;size&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'foo'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'bar'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_true&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;thing&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;self_keys&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;include?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'baz'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;should be_false&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And here is the output of running the above tests:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;$ rspec spec&lt;span style="color: #d2cd86;"&gt;/&lt;/span&gt;models&lt;span style="color: #d2cd86;"&gt;/&lt;/span&gt;thing_spec&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;rb&lt;br /&gt;&lt;br /&gt;Thing&lt;br /&gt;&amp;nbsp; no inheritance&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt; object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be empty&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; adding properties to object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like simple properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; adding fields to object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like simple properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; inheritance&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt; object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like simple properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; adding properties to object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like nested properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; b2&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bye&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; adding fields to object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like nested properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; b2&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bye&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;4&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;5&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"hello"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bye"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; adding redundant fields to object&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; it should behave like simple properties&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bar&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar2"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar2"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; baz&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #e66170; font-weight: bold;"&gt;not&lt;/span&gt; be &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; should &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"bar2"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Finished &lt;span style="color: #e66170; font-weight: bold;"&gt;in&lt;/span&gt; &lt;span style="color: #009f00;"&gt;0.06217&lt;/span&gt; seconds&lt;br /&gt;&lt;span style="color: #008c00;"&gt;106&lt;/span&gt; examples, &lt;span style="color: #008c00;"&gt;0&lt;/span&gt; failures&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-380537270718108865?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/380537270718108865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=380537270718108865' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/380537270718108865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/380537270718108865'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/09/unit-testing-simple-property-model.html' title='Unit Testing a Simple Property Model'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-2409066303298316953</id><published>2011-08-29T09:00:00.003-04:00</published><updated>2011-08-29T09:00:08.459-04:00</updated><title type='text'>Simple Property Model in Ruby</title><content type='html'>I've &lt;a href="http://madkingsmusings.blogspot.com/2010/08/learning-javascript.html"&gt;talked before&lt;/a&gt;&amp;nbsp;about how JavaScript follows what Steve Yegge calls the &lt;a href="http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html"&gt;Universal Design Pattern&lt;/a&gt;. I can see the value of associating an arbitrary number of properties with an object, and to be able to say that one object is "like" another object the way JavaScript's prototype property works. I even have ideas for using this. However, I don't do my server side programming in JavasScript. Here is my first cut at a very bare bones property model using Ruby.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Code&lt;/span&gt;&lt;/div&gt;&lt;div&gt;First I'll show you the class that stores a property. It is a just a simple key/value pair.&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; Property&lt;br /&gt;  attr_accessor :name, :value&lt;br /&gt;  &lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; initialize&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;name, value&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;    @name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; name&lt;br /&gt;    @value &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; value&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Next, is my code for the Object class (which I call &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Thing&lt;/span&gt;):&lt;/div&gt;&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; Thing&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; attr_accessor :prototype, :type, :properties&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; initialize&lt;br /&gt;&amp;nbsp; &amp;nbsp; @properties &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; self_keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; @properties&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; keys&lt;br /&gt;&amp;nbsp; &amp;nbsp; self_keys | &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;@prototype ? @prototype&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;keys : &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; []&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;key&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; @properties&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;key&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;value &lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; @properties&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;has_key? key&lt;br /&gt;&amp;nbsp; &amp;nbsp; @prototype ? @prototype&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;key&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; : &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; []&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;key, value&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; @properties&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;has_key? key&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; @properties&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;key&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;value &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; value&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; @properties&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;key&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Property&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;key, value&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; to_hash&lt;br /&gt;&amp;nbsp; &amp;nbsp; hash &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @prototype ? @prototype&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_hash : &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @properties&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;each_value &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;|&lt;span style="color: #e66170; font-weight: bold;"&gt;p&lt;/span&gt;| hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;p&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;p&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;value&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; hash&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; method_missing&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;method, &lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;args&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; method&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_s&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;name&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;end_with?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'='&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;name&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt;&lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; args&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As you can see a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Thing&lt;/span&gt;" consists of a type, a prototype object, and a map of key/property pairs. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Property&lt;/span&gt; lookups (which can be done using either array type syntax or the dot syntax of methods and fields) first look at a thing's properties, and then at its prototype's properties. All in all, it is a fairly simple pair of classes.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Thoughts&lt;/span&gt;&lt;/div&gt;&lt;div&gt;There are a couple of decisions here that seem odd, so I'll explain my thinking.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Thing&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Why did I call the Object type &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Thing&lt;/span&gt;? Well there is already an &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Object&lt;/span&gt; class in Ruby and I didn't want to worry about naming collisions. Like object, "thing" is a generic enough word that if I want to create a class with that name, I should probably rethink my design. So it satisfies the qualifications of describing "something" without causing any likely naming collisions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Property&lt;/span&gt;&lt;br /&gt;Why do I have a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Property&lt;/span&gt; class&amp;nbsp;rather than doing the more straightforward thing of having &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Thing&lt;/span&gt; just containing a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Hash&lt;/span&gt; of key/values? Well, this is an example of me violating the &lt;a href="http://en.wikipedia.org/wiki/You_ain't_gonna_need_it"&gt;YAGNI principle&lt;/a&gt;. I am going to want to persist this information to a database so I am envisioning a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Thing&lt;/span&gt; table and a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Property&lt;/span&gt; table. My assumption is that when I do that, I'll want the key/value pairs broken out into their own &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Property&lt;/span&gt; class. So I am (perhaps foolishly) breaking that out ahead of time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;method_missing&lt;/span&gt;&lt;br /&gt;Like JavaScript, I want to be able access properties via either array syntax (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;thing['property']&lt;/span&gt;) or method syntax (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;thing.property&lt;/span&gt;). Ruby provides the functionality to &lt;a href="http://rubylearning.com/satishtalim/ruby_method_missing.html"&gt;handle unknown method names&lt;/a&gt;, via the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;method_missing&lt;/span&gt; method. As you can see, I wrote a bare bones implementation that does no error checking. If it gets passed a method that ends with '=', it assumes that it is a property assignment, otherwise it assumes it is a property read. I suppose in the future I could/should add error checking like that there are the right number of arguments to the method. I should probably also implement &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;respond_to&lt;/span&gt; so it will &lt;a href="http://www.dan-manges.com/blog/30"&gt;return true for any property that exists&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Lack of Functionality&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Do these classes actually buy me anything? I'm not sure. I am sure that I am going to need a bunch of additional functionality (like database persistance) before they are really useful to me. But I have to start somewhere. So rather than blog about a fully baked solution, I've decided to show you some of the raw ingredients as I go. I am hoping that this gives me a good starting point.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-2409066303298316953?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/2409066303298316953/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=2409066303298316953' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2409066303298316953'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2409066303298316953'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/08/simple-property-model-in-ruby.html' title='Simple Property Model in Ruby'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-5587325058975981347</id><published>2011-08-22T09:00:00.006-04:00</published><updated>2011-08-22T09:00:12.301-04:00</updated><title type='text'>Wrong is Better Than Vague</title><content type='html'>&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Vague&lt;/span&gt;&lt;br /&gt;I've got this idea for the next killer app. It's this game, you see, that'll leverage the unique properties of tablets. Do you want to work on implementing it with me?&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;What's the game? &amp;nbsp;&lt;i&gt;Oh, something fun.&lt;/i&gt;&lt;br /&gt;So this will be for the iPad? &lt;i&gt;That and other tablets.&lt;/i&gt;&lt;br /&gt;Who's going to play it? &amp;nbsp;&lt;i&gt;Everyone.&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Wrong&lt;/span&gt;&lt;br /&gt;I am going to create a 3D minesweeper for the iPad and I'll sell it for $473.26 per copy. Minesweeper is already popular on the Windows, and there is this huge market of iPad owners who have already shown they are willing to pay a lot for gadgets. And with the 3D angle we can get rich off of this. Do you want to work on it with me?&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;The Difference&lt;/span&gt;&lt;br /&gt;The problem with vague is that most ideas sound either good or bad depending on the listener's optimism. Vague is how a small project turns into a years long boondoggle, or how the next billion dollar idea gets missed.&lt;br /&gt;&lt;br /&gt;In my experience most new ideas from management types (and a disturbing number of research scientists) are of the vague variety. They have these great pie in the sky ideas, but if you try to actually evaluate them for engineering or business practicality, you can't. Without details there is no way to determine if the idea is good or completely infeasible.&lt;br /&gt;&lt;br /&gt;Take the vague example above. Would you work on it? Is it going to be successful? Who knows. What about the wrong example? Of course you wouldn't work on it, as is. But because it is concrete, it could be improved.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Nobody pays $400 for apps! &amp;nbsp;&amp;nbsp;&lt;i&gt;Oh, then I guess we can charge $4.95.&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote&gt;The iPad doesn't support 3D. &amp;nbsp;&amp;nbsp;&lt;i&gt;Hmm.. then maybe we should target those &lt;a href="http://3d-mobile-phones.org/"&gt;cell phones that do&lt;/a&gt;.&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote&gt;Minesweeper really doesn't lend itself to 3D. &amp;nbsp; &amp;nbsp;&lt;i&gt;Well, what about this other [fill in specific details here] game?&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote&gt;How much work will this be? &amp;nbsp;&amp;nbsp;&lt;i&gt;Oh, given those details it'll take about 3 months.&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote&gt;So how many do have to sell to make it worthwhile? &amp;nbsp; &amp;nbsp;&lt;i&gt;Oh, my time is cheap, probably just 10 copies.&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote&gt;Then you'll probably be successful, but I don't think I'll help you.&lt;/blockquote&gt;As you can see, with concreteness, even wrong concreteness, decisions can be made in an informed way.&lt;br /&gt;&lt;br /&gt;Even more important than concrete plans, are concrete implementations. There's a reason people create research prototypes, whether it is for a physical product or software. You can learn so much more from what goes wrong than you can from just the high level ideas.&lt;br /&gt;&lt;br /&gt;So the next time you have an idea, make it concrete. A wrong implementation is worth much more than just the vague idea itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-5587325058975981347?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/5587325058975981347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=5587325058975981347' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5587325058975981347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5587325058975981347'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/08/wrong-is-better-than-vague.html' title='Wrong is Better Than Vague'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-831352977063850383</id><published>2011-08-15T09:00:00.001-04:00</published><updated>2011-08-15T09:00:09.464-04:00</updated><title type='text'>Emailing Server Errors</title><content type='html'>I've talked about &lt;a href="http://madkingsmusings.blogspot.com/2011/07/checking-web-application-health.html"&gt;checking the server's health&lt;/a&gt;, but its even nicer if the server lets you know if it has a problem. In particular, you don't want user's experiencing errors and for you to remain in blissful ignorance. A simple fix is to have your server email you whenever a user encounters an error. Here is how I did this in rails.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Catch the Error&lt;/span&gt;&lt;br /&gt;The first thing to do is to capture any exceptions that get thrown. This can be done easily by adding the following line to your &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ApplicationController&lt;/span&gt;.&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;unless&lt;/span&gt; Rails&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;application&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;config&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;consider_all_requests_local &lt;span style="color: #9999a9;"&gt;# don't rescue on local testing&lt;/span&gt;&lt;br /&gt;&amp;nbsp; rescue_from Exception, :with &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; :handle_error&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As of this writing, apparently Rail 3 &lt;a href="http://techoctave.com/c7/posts/36-rails-3-0-rescue-from-routing-error-solution"&gt;doesn't support 404 errors this way&lt;/a&gt;, so a fix for this is to add a catch-all route at the bottom of your &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;config/routes.rb&lt;/span&gt; that looks something like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #9999a9;"&gt;# this must be the LAST rule in the file&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #9999a9;"&gt;# it allows our custom 404 error to execute&lt;/span&gt;&lt;br /&gt;match &lt;span style="color: #00c4c4;"&gt;'*path'&lt;/span&gt;, :to &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;'application#routing'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Handle the Error&lt;/span&gt;&lt;br /&gt;Now that I have caught unhandled exceptions and 404 errors, you need to do something with them. These methods in the the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ApplicationController&lt;/span&gt; are the ones called from the above code:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; routing&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;exception &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; record_error &lt;span style="color: #00c4c4;"&gt;"404 error"&lt;/span&gt;, exception&lt;br /&gt;&amp;nbsp; render :template &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #00c4c4;"&gt;"/errors/404.html"&lt;/span&gt;, :status &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #008c00;"&gt;404&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; handle_error&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;exception&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; msg &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; exception ? exception&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;message : &lt;span style="color: #00c4c4;"&gt;"unknown error"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; record_error&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;msg, exception&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; render :template &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #00c4c4;"&gt;"/errors/500.html"&lt;/span&gt;, :status &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #008c00;"&gt;500&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; record_error&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;msg, exception&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; Rails&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;application&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;config&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;consider_all_requests_local &lt;span style="color: #9999a9;"&gt;# don't email during local testing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; referrer &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; request&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;env&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'HTTP_REFERER'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp; url &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; request&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;url&lt;br /&gt;&amp;nbsp; app_name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Rails&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;application&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_s&lt;br /&gt;&amp;nbsp; user &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; current_user&lt;br /&gt;&amp;nbsp; name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; user ? user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;username : &lt;span style="color: #00c4c4;"&gt;"UNKNOWN"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; error_msg &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; msg&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; exception&lt;br /&gt;&amp;nbsp; &amp;nbsp; error_msg &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;" #{exception.message}\n#{exception.backtrace.join '\n'}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; ip &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; request&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;remote_ip&lt;br /&gt;&amp;nbsp; remote_ip &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; request&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;env&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'HTTP_X_FORWARDED_FOR'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; logger&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;warn &lt;span style="color: #00c4c4;"&gt;"Error #{error_msg} for url [#{url}] coming from url [#{referrer}] from user [#{user}] at ip [#{ip}] forwarded from ip [#{remote_ip}]"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; mail &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; ErrorMailer&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;log_error&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;msg, app_name, url, referrer, ip, remote_ip, name, exception&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; mail&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;deliver&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;You can make the 404 and 500 error pages that these methods render be whatever you like, though it is suggested that you &lt;a href="http://www.codinghorror.com/blog/2007/03/creating-user-friendly-404-pages.html"&gt;make them useful&lt;/a&gt;. The record_error method that these methods calls, collect a bunch of information about the request, log them (in case mailing fails), and then emails them. The emailing is done using &lt;a href="http://guides.rubyonrails.org/action_mailer_basics.html"&gt;Rails mailing capability&lt;/a&gt;. Since the actual configuration for talking to the mail server will depend on your situation, you should read the &lt;a href="http://guides.rubyonrails.org/action_mailer_basics.html"&gt;documentation&lt;/a&gt; for that.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Emailer&lt;/span&gt;&lt;br /&gt;To send the email, I created &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mailers/error_mailer.rb&lt;/span&gt; which is analogous to a controller. All it does is package all the variables into instance variables that the view will have:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; ErrorMailer &amp;lt; ActionMailer::Base&lt;br /&gt;&amp;nbsp; default :from &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #00c4c4;"&gt;"myApplication@myurl.com"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; helper :application&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; log_error&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;subject, app_name, url, referrer, ip, remote_ip, user_name, error&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @app_name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; app_name&lt;br /&gt;&amp;nbsp; &amp;nbsp; @url &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; url&lt;br /&gt;&amp;nbsp; &amp;nbsp; @referrer &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; referrer&lt;br /&gt;&amp;nbsp; &amp;nbsp; @ip &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; ip&lt;br /&gt;&amp;nbsp; &amp;nbsp; @remote_ip &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; remote_ip&lt;br /&gt;&amp;nbsp; &amp;nbsp; @user_error &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; user_error&lt;br /&gt;&amp;nbsp; &amp;nbsp; @user_name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; user_name&lt;br /&gt;&amp;nbsp; &amp;nbsp; @error &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; error&lt;br /&gt;&amp;nbsp; &amp;nbsp; mail :to &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; &lt;span style="color: #00c4c4;"&gt;"myAddress@somewhere.com"&lt;/span&gt;, :subject&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;"RAILS ERROR in #{@app_name}: #{subject}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The view&amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/error_mailer/log_error.text.haml&lt;/span&gt;) turns this information into an email message:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;There was an error &lt;span style="color: #e66170; font-weight: bold;"&gt;in&lt;/span&gt; app &lt;span style="color: #9999a9;"&gt;#{@app_name} at time #{Time.now}.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;URL: &amp;nbsp;&lt;span style="color: #9999a9;"&gt;#{@url}&lt;/span&gt;&lt;br /&gt;Referrer: &amp;nbsp;&lt;span style="color: #9999a9;"&gt;#{@referrer}&lt;/span&gt;&lt;br /&gt;IP: &amp;nbsp;&lt;span style="color: #9999a9;"&gt;#{@ip}&lt;/span&gt;&lt;br /&gt;IP via a portal: &amp;nbsp;&lt;span style="color: #9999a9;"&gt;#{@remote_ip}&lt;/span&gt;&lt;br /&gt;User: &lt;span style="color: #9999a9;"&gt;#{@user_name}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; @error&lt;br /&gt;&amp;nbsp; Error &lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt;: &lt;span style="color: #9999a9;"&gt;#{@error.class}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; Error message: &lt;span style="color: #9999a9;"&gt;#{@error.message}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; Error Stack Trace:&lt;br /&gt;&amp;nbsp; &lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; @error&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;backtrace&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |line|&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; line&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp; No exception to log&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Warning&lt;/span&gt;&lt;br /&gt;I do have one piece of advice: Make sure the subject you give your error emails is something very distinct that you can write mail filter rules. After I deployed a similar solution at work, the security teams scanner found my app and proceeded to run through its entire litany of tests against the server. I came in the next morning to more than 4,000 emails from 404 errors. The fact that I had put the entire development team as the recipients of these emails was just the icing on the cake.&lt;br /&gt;&lt;br /&gt;Maybe emailing every error isn't great, since you can look in the log files for the same information. However, I find the immediacy and in your face nature of an email is good for making sure that errors get fixed and aren't just silently experienced by your users (and ex users). However, be aware that if something goes wrong, you have the potential to receive a lot of email. (not to mention choice comments from anyone else who is also getting those emails).&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-831352977063850383?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/831352977063850383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=831352977063850383' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/831352977063850383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/831352977063850383'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/08/emailing-server-errors.html' title='Emailing Server Errors'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-9083226080217984063</id><published>2011-08-08T09:00:00.003-04:00</published><updated>2011-08-08T09:00:19.523-04:00</updated><title type='text'>One year of regular posts</title><content type='html'>Though I started this blog years ago, it was one year ago that I started posting regularly. This was partially inspired by Jeff Atwood's advice to &lt;a href="http://www.codinghorror.com/blog/2007/10/how-to-achieve-ultimate-blog-success-in-one-easy-step.html"&gt;post regularly&lt;/a&gt;. I have no idea how he regularly posted 6 times a week given that I have a hard time posting once a week. And I think that Syd (a commenter on that post) hits the nail on the head that there is more to being&amp;nbsp;a popular blogger&amp;nbsp;than just being reliable. However, being reliable is definitely a prerequisite.&lt;br /&gt;&lt;br /&gt;So how have I done? Well, I missed two weeks in the past year. And I feel like I am struggling each week to keep up. However, &lt;a href="http://www.google.com/analytics/"&gt;Google Analytics&lt;/a&gt;&amp;nbsp;shows that I've been at least a little bit successful. I've trended up pretty consistently since I started posting regularly.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-fRLnNVINcMI/Tj85OUbwxWI/AAAAAAAABBc/cc_0WUvc62M/s1600/usage-2011-08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="195" src="http://2.bp.blogspot.com/-fRLnNVINcMI/Tj85OUbwxWI/AAAAAAAABBc/cc_0WUvc62M/s640/usage-2011-08.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;100 page views a week is far from a popular blog. But it is a number that is high enough that I know that it isn't just me and my closest friends reading my blog. So that is some measure of success.&lt;br /&gt;&lt;br /&gt;If you've been following my blog you've noticed that I have two different types of posts, my&amp;nbsp;philosophical&amp;nbsp;posts and my practical/tutorial posts. When I started this blog, my goal was to post primarily philosophical posts and to generate a discussion on each post. My secret goal was to learn more from my comments than I put into my posts. Unfortunately, so far, I don't get many comments, so that isn't working out. At some point I decided to also write tutorial type posts describing how I solved a specific problem. I've learned much from others who have posted what they know, so I feel I should give back. Based on Google Analytics, the majority of people who see my blog come to view these posts. Unfortunately, if the readers don't leave comments, I have no idea if what I posted was useful or just a waste of time. I can only hope that some people are finding it useful. Even more, I hope that some of these people become regular readers (and commenters) on my more philosophical posts. I am the eternal optimist and still hope to achieve my original goal.&lt;br /&gt;&lt;br /&gt;Of course, to accomplish that, I need to continue Jeff Atwood's advice and post regularly. While I have plenty of ideas, it is somewhat daunting to me to actually turn those ideas into posts each week. Well, here's hoping that next August I get to bore you all with another post like this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-9083226080217984063?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/9083226080217984063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=9083226080217984063' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/9083226080217984063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/9083226080217984063'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/08/one-year-of-regular-posts.html' title='One year of regular posts'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-fRLnNVINcMI/Tj85OUbwxWI/AAAAAAAABBc/cc_0WUvc62M/s72-c/usage-2011-08.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-4290253772827236168</id><published>2011-08-01T09:00:00.001-04:00</published><updated>2011-08-01T09:00:00.394-04:00</updated><title type='text'>Documentation and Context</title><content type='html'>A few weeks ago Scott Adams (of Dilbert fame) wrote a &lt;a href="http://www.dilbert.com/blog/entry/author_by_relocation/"&gt;blog post&lt;/a&gt;&amp;nbsp;about the context of a message. One of his points was that the context that a message is presented in affects how the message is received. Saying the same words at a standup comedy club and at a funeral won't have the same affect. This is related to a thought I've long had about software design documentation. We all have a context in our mind, but that of a reader is probably different.&lt;br /&gt;&lt;br /&gt;A specific example of this difference came up at work recently. Asok, Alice, and I (not their real names) were discussing a new feature we wanted added. The feature was something Alice and I needed to support our project, but it required a change in Asok's code. Over the course of an hour long meeting Alice and I kept suggesting changes to Asok's code and he kept telling us that what we were suggesting didn't make any sense.&lt;br /&gt;&lt;br /&gt;So why did Asok keep telling us our suggestions made no sense? It wasn't arrogance, he was right. Our suggestions didn't make sense in the context of his code. It was a lack of understanding on Alice and my's part. We were looking at database and classes, and based on their names we were making certain assumption that turned out to be wrong.&lt;br /&gt;&lt;br /&gt;Why didn't Alice and I understand Asok's design well enough to make good suggestions? We both have plenty of experience in the field, so it wasn't that. It would be easy to blame poor naming of databases and classes on Asok's part, but now that I understand it, I'm not sure I can come up with anything better. Really it was just a matter of context. Asok had worked on this system for months, and everything fit into a model he had in his head. Alice and I didn't have that model, so we made up our own. As often happens, the model we assumed didn't match reality.&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;How should [we] document our designs&lt;/span&gt;This, to me, is the big problem of software design documentation. I have been on both sides of this problem. I have seen people completely mangle my code because they didn't see the "correct" ways and places to extend it. I have also mangled others people's code for the same reason. This is how unmaintainable&amp;nbsp;behemoths&amp;nbsp;get created. The challenge is: how should Asok, Alice, or I document our designs such that the context we have in our head is conveyed to the next person?&lt;br /&gt;&lt;br /&gt;Some people say that the &lt;a href="http://www.commonsense4commonpeople.net/2008/11/the-code-is-the-documentation.html"&gt;code is the documentation&lt;/a&gt;. While the code is the reality of today, the context of the code shows how to extend, modify, and use that code. Sharing this context from the code writer's mind is crucial to code being able to be maintained beyond the original author.&lt;br /&gt;&lt;br /&gt;So, I guess this is another one of those posts where I point out a problem and have no solution. Do you have a solution?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-4290253772827236168?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/4290253772827236168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=4290253772827236168' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4290253772827236168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4290253772827236168'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/08/documentation-and-context.html' title='Documentation and Context'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-4025531700647759813</id><published>2011-07-25T09:00:00.001-04:00</published><updated>2011-07-25T09:00:12.079-04:00</updated><title type='text'>Checking Web Application Health</title><content type='html'>There are certain things you want to know about your web application to make sure it has started correctly and to help diagnose problems.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it up?&lt;/li&gt;&lt;li&gt;Can it connect to the database(s)?&lt;/li&gt;&lt;li&gt;Can it connect to the web service(s)?&lt;/li&gt;&lt;li&gt;Can it do any other actions that may independently break?&lt;/li&gt;&lt;li&gt;What are the server settings?&lt;/li&gt;&lt;li&gt;What version of the code is running?&lt;/li&gt;&lt;/ul&gt;The first question is easy to answer? But what about the others? You can obviously determine if the other dependencies are up by going to the appropriate place in your application, but it'd be nice to have a single location to check all of these. And what about server settings or code version? Ever had a bug reported that you know you fixed? It's nice to also be able to make sure that you really are running the version you think you are.&lt;br /&gt;&lt;br /&gt;Here's how I've accomplished this on a &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; project.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Single Location&lt;/span&gt;&lt;br /&gt;First, I created a controller with a single page:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span class="Apple-style-span" style="font-family: monospace;"&gt;prompt&lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt; rails generate controller Admin index&lt;/span&gt;&lt;/pre&gt;Here is the beginning of the controller class.&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; AdminController &amp;lt; ApplicationController&lt;br /&gt;&amp;nbsp; before_filter :require_admin&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; index&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As you can see, I've protected it, so only administrative users can view this page. Here is the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/admin/index.html.haml&lt;/span&gt; where I show server state.&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;This is the status page&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;br&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;h2 Version&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;pre&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;@&lt;/span&gt;version&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;h2 Environment&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;br&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;b RAILS_ENV&lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; ENV&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="background-attachment: initial; background-clip: initial; background-color: #dd0000; background-image: initial; background-origin: initial; color: white;"&gt;'RAILS_ENV'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;br&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;hr&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;h2 Site Tests&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;test&lt;br /&gt;&amp;nbsp; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; button_to &lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;Test DB&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;action&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;test_db&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;remote&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;true&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;onClick&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;startTest()&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;hr&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;%&lt;/span&gt;h2 Test Output&lt;br /&gt;&lt;span style="color: #008073;"&gt;#testOutput&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;javascript&lt;br /&gt;&amp;nbsp; function startTest&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; $&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;#testOutput&lt;/span&gt;&lt;span style="color: #02d045;"&gt;"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;replaceWith&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="background-attachment: initial; background-clip: initial; background-color: #dd0000; background-image: initial; background-origin: initial; color: white;"&gt;'&amp;lt;div id="testOutput"&amp;gt;Loading...&amp;lt;/div&amp;gt;'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Dependency Tests&lt;/span&gt;&lt;br /&gt;I've implemented each test as a separate Ajax call. In the view code above, this everything from "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Site Tests&lt;/span&gt;" on down. This consists of the buttons (one for each test, just one here), a place to show the output, and the javascript to make this work. For each test, I just add a new button in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.test&lt;/span&gt; div. As you can see in the code above, the button makes an Ajax call (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;:remote=&amp;gt;true&lt;/span&gt;) and calls a javascript function to show that the result is loading. It is up to the controller to then update the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;testOutput&lt;/span&gt; div with output status.&lt;br /&gt;&lt;br /&gt;In my &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;admin_controller.rb&lt;/span&gt; I have a function for each button that looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; test_db&lt;br /&gt;&amp;nbsp; standard_test &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #9999a9;"&gt;# insert test code here&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The helper function &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;standard_test&lt;/span&gt;&amp;nbsp;looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; standard_test&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;begin&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;yield&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @success &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;rescue&lt;/span&gt; Exception &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; e&lt;br /&gt;&amp;nbsp; &amp;nbsp; @success &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @exception &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; e&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp; respond_to &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;|&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;html &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; flash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:error&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"This should've been ajax"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; redirect_to admin_index_path&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;js &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; render &lt;span style="color: #00c4c4;"&gt;'test_output'&lt;/span&gt;, :layout&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;All it does is executes the test code (via the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;yield&lt;/span&gt; statement) and catches any thrown exceptions. If there is no exception, it considers the test a success. Assuming it was called via an Ajax call, it then renders the javascript template &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;test_output.js.haml&lt;/span&gt; which is simply:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #d2cd86;"&gt;!=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"$('#testOutput').replaceWith('#{escape_javascript render(:partial=&amp;gt;'test_output')}')"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As you can see, this merely replaces the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;testOutput&lt;/span&gt; div with what is rendered by the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_test_output.html.haml&lt;/span&gt; partial:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;#testOutput&lt;br /&gt;&amp;nbsp; - if @success&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b SUCCESS&lt;br /&gt;&amp;nbsp; - else&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b &lt;span style="color: #e66170; font-weight: bold;"&gt;ERROR&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; %br&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b &lt;span style="color: #e66170; font-weight: bold;"&gt;Error&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;Class&lt;/span&gt;:&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @exception.class&lt;br /&gt;&amp;nbsp; &amp;nbsp; %br&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b &lt;span style="color: #e66170; font-weight: bold;"&gt;Error&lt;/span&gt; Message:&lt;br /&gt;&amp;nbsp; &amp;nbsp; %&lt;span style="color: #e66170; font-weight: bold;"&gt;pre&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @exception.message&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b &lt;span style="color: #e66170; font-weight: bold;"&gt;Error&lt;/span&gt; Stack Trace:&lt;br /&gt;&amp;nbsp; &amp;nbsp; %&lt;span style="color: #e66170; font-weight: bold;"&gt;pre&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - @exception.backtrace.each do |line|&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; line&lt;br /&gt;&amp;nbsp; &amp;nbsp; %b &lt;span style="color: #e66170; font-weight: bold;"&gt;Error&lt;/span&gt; Inspect&lt;br /&gt;&amp;nbsp; &amp;nbsp; %&lt;span style="color: #e66170; font-weight: bold;"&gt;pre&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @exception.inspect&lt;/pre&gt;Of course, don't forget to make sure your routes are in your &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;config/routes.rb.&lt;/span&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;get &lt;span style="color: #00c4c4;"&gt;'admin/index'&lt;/span&gt;&lt;br /&gt;post &lt;span style="color: #00c4c4;"&gt;'admin/my_test'&lt;/span&gt; &lt;span style="color: #9999a9;"&gt;# separate route for each test&lt;/span&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Version&lt;/span&gt;&lt;br /&gt;Including version information is going to be very specific to your environment. We use &lt;a href="http://mercurial.selenic.com/"&gt;Mercurial&lt;/a&gt;&amp;nbsp;for our version control, and since we have a number of Java project, we use &lt;a href="http://ant.apache.org/"&gt;ant&lt;/a&gt;&amp;nbsp;for our deployment process. Here is how I connected these together.&lt;br /&gt;&lt;br /&gt;In the ant deployment script, I added a target that looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #ff8906;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #f6c1d0;"&gt;target&lt;/span&gt; name&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;write.hg.version&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #ff8906;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #ff8906;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #f6c1d0;"&gt;exec&lt;/span&gt; executable&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;hg&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt; dir&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;${project}&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt; output&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;${project}/config/version.txt&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #ff8906;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style="color: #ff8906;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #f6c1d0;"&gt;arg&lt;/span&gt; value&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;summary&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt; &lt;span style="color: #ff8906;"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #fb8400;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #f6c1d0;"&gt;exec&lt;/span&gt;&lt;span style="color: #fb8400;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #fb8400;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #f6c1d0;"&gt;target&lt;/span&gt;&lt;span style="color: #fb8400;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;All that is left is to get the contents of this &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;version.txt&lt;/span&gt; file into the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@version&lt;/span&gt; variable that is displayed by &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;index.html.haml&lt;/span&gt;. This is done by simply having the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;index&lt;/span&gt; method in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;admin_controller.rb&lt;/span&gt; read in the file.&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; index&lt;br /&gt;&amp;nbsp; name &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; File&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;join&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;RAILS_ROOT, &lt;span style="color: #00c4c4;"&gt;'config'&lt;/span&gt;, &lt;span style="color: #00c4c4;"&gt;'version.txt'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; File&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;exists? name&lt;br /&gt;&amp;nbsp; &amp;nbsp; f &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; File&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;name&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @version &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;read&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @version &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"Unknown version"&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;This really wasn't too much work, and now I have a single page that I can go to and at a glance see what version of the code was deployed and which rails environment is being used. Adding new tests is just a simple matter of adding a new button to the view, which calls a new test method in the controller. By leveraging the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;standard_test&lt;/span&gt; method, new tests can consist of just the code they are intended to test.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-4025531700647759813?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/4025531700647759813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=4025531700647759813' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4025531700647759813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4025531700647759813'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/07/checking-web-application-health.html' title='Checking Web Application Health'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6882409334321476841</id><published>2011-07-18T09:00:00.000-04:00</published><updated>2011-07-18T09:00:20.995-04:00</updated><title type='text'>Windows Command Line Cut &amp; Paste Rant</title><content type='html'>Just a quick rant this week. When selecting text in the windows command console, why does it do a "block" mark rather than a line based mark?!?! In every other text based environment I've used, including Windows Notepad and MS Word, if you start highlighting text in the middle of one line and move down to the middle of the next line it select the end of the first line and the beginning of the next line. &amp;nbsp;i.e. all the text that starts where you started the mark and ends where you ended the mark. For some reason the Windows Command shell only marks the text that is actually in the box, i.e. a couple characters in the middle of both lines.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-o0u2ZfC-nxA/TiGYMU5pHnI/AAAAAAAABA4/8W4Ev37DFbo/s1600/CommandMark.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-o0u2ZfC-nxA/TiGYMU5pHnI/AAAAAAAABA4/8W4Ev37DFbo/s1600/CommandMark.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Is there a time that this behavior is useful? If you know, please share it with me.&lt;br /&gt;&lt;br /&gt;For me, I often want to copy either a long command line I ran, or the output of a command, to put in an email, IM, a wiki, or even to paste into another terminal to run. But if the line is wrapped, then to make sure I get everything I want, I have to also copy the command prompt. This means I have to add an intermediate step of editing my selection before I can send it.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-oI50eTXVQUk/TiGYdIrYlFI/AAAAAAAABA8/ry24TouFVeA/s1600/notepadCutPaste.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-oI50eTXVQUk/TiGYdIrYlFI/AAAAAAAABA8/ry24TouFVeA/s1600/notepadCutPaste.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;What's even more bizzarre to me, is that in searching on the web, I can't even find any other comments about this behavior. Nor can I find a way to change this. Am I the only one who finds this annoying? More importantly, does anyone know how to change this behavior?&lt;br /&gt;&lt;br /&gt;(I know, I know. I shouldn't be using Windows, and I shouldn't be cutting and pasting. Well, I have to admit that I sometimes do both, and I was doing a lot of both this past week.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6882409334321476841?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6882409334321476841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6882409334321476841' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6882409334321476841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6882409334321476841'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/07/windows-command-line-cut-paste-rant.html' title='Windows Command Line Cut &amp; Paste Rant'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-o0u2ZfC-nxA/TiGYMU5pHnI/AAAAAAAABA4/8W4Ev37DFbo/s72-c/CommandMark.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-1541005723949760097</id><published>2011-07-11T09:00:00.000-04:00</published><updated>2011-07-11T09:00:13.227-04:00</updated><title type='text'>Attack of the Clones</title><content type='html'>As I &lt;a href="http://madkingsmusings.blogspot.com/2011/06/too-slow.html"&gt;mentioned&lt;/a&gt;, I was recently at &lt;a href="http://2011.icse-conferences.org/"&gt;ICSE&lt;/a&gt;. One of the big topics there was Clones. How do you detect them? How do you remove them? The talk that I found most interesting on the topic was one where they tried to &lt;a href="http://2011.icse-conferences.org/content/frequency-and-risks-changes-clones"&gt;measure how bad clones really are&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://i452.photobucket.com/albums/qq250/Demonic-Genius/stormtrooperarmy.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="179" src="http://i452.photobucket.com/albums/qq250/Demonic-Genius/stormtrooperarmy.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;First, the definition for those of you aren't familiar with the term clone in the context of software engineering. (I wasn't before this conference). A clone is just what it sounds like, duplicated code. Clones are most often created via cut and paste.&lt;br /&gt;&lt;br /&gt;The idea of removing duplicated code through common modules, whether they are libraries, classes, functions, procedures, or macros is an old one. Probably about as old as programming. In recent years, removing duplicated code has even got a hip acronym: &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY&lt;/a&gt;. The idea that clones are bad was basically a given at this conference. I have pretty much internalized that idea, so this didn't seem strange to me at all.&lt;br /&gt;&lt;br /&gt;So how bad are they? Well, as I mentioned above, there was &lt;a href="http://portal.acm.org/citation.cfm?id=1985836"&gt;research&lt;/a&gt; that tried to answer this question. Researchers analyzed the version control history of a few different projects and tracked all of the clones in these projects. They then tried to evaluate some of the problems. For example, one of the problems with clones is that if you change the code, you have to remember to do that change in multiple locations. What they found was that in the vast majority of the time, clones either never changed, or only changed once. Meaning that the majority of the time, the work of refactoring the code to remove the clones would be at least as large as the work of just maintaining them.&lt;br /&gt;&lt;br /&gt;What about the situation where one clone is modified, but another copy doesn't get updated. Well, they found that this is also very rare, and a lot of the time when this does happen, its intentional. And even of the times when they are unintentionally out of sync, the resulting code is typically not a bug.&lt;br /&gt;&lt;br /&gt;So what does all this mean? I don't know. Maybe their sample projects weren't representative of most software projects. Or maybe, just like most other engineering decisions, there are trade offs, and there isn't a 100% right answer, independent of context. I still find that clones offend my sense of software ascetics most of the time, but I am trying to be a little more open-minded and be critical about what and when I refactor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-1541005723949760097?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/1541005723949760097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=1541005723949760097' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1541005723949760097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1541005723949760097'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/07/attack-of-clones.html' title='Attack of the Clones'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-505845179582828437</id><published>2011-07-04T09:00:00.014-04:00</published><updated>2011-07-04T09:00:12.368-04:00</updated><title type='text'>GCJ 2011 T-Shirt</title><content type='html'>Due to the holiday weekend, I am just making a short post this week.&lt;br /&gt;&lt;br /&gt;Last week I mentioned that I got knocked out of the &lt;a href="http://community.topcoder.com/tco11/algorithm/"&gt;TCO 2011&lt;/a&gt;&amp;nbsp;competition, and so will get no shirt for it. Well, a couple days later FedEx delivered my &lt;a href="http://code.google.com/codejam/"&gt;Google Code Jam&lt;/a&gt; 2011 t-shirt. It's not as cool as getting the trip to Japan for the finals, but I guess I'll take what I can get. Since &lt;a href="http://madkingsmusings.blogspot.com/2011/06/too-slow.html?showComment=1308329901852#c901489557281530445"&gt;a picture was requested&lt;/a&gt;, here it is. And, no Alan, programming contests don't completely eliminate the &lt;a href="http://madkingsmusings.blogspot.com/2011/06/no-t-shirt-for-me.html?showComment=1309229634512#c3433002833194622196"&gt;need to go clothes shopping&lt;/a&gt;, though I do have around two dozen t-shirts plus a nice winter coat and hat from various programming contests.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-eWthLQs-9_M/Tg_eInp6YVI/AAAAAAAAAuk/u-8Axu0QC18/s1600/IMG_20110702_103255.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="476" src="http://2.bp.blogspot.com/-eWthLQs-9_M/Tg_eInp6YVI/AAAAAAAAAuk/u-8Axu0QC18/s640/IMG_20110702_103255.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-505845179582828437?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/505845179582828437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=505845179582828437' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/505845179582828437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/505845179582828437'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/07/gcj-2011-t-shirt.html' title='GCJ 2011 T-Shirt'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-eWthLQs-9_M/Tg_eInp6YVI/AAAAAAAAAuk/u-8Axu0QC18/s72-c/IMG_20110702_103255.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-5787458583994313384</id><published>2011-06-27T09:00:00.002-04:00</published><updated>2011-06-27T09:00:10.728-04:00</updated><title type='text'>No T-Shirt For Me</title><content type='html'>This past Saturday my brain turned off and I failed to get even a single problem right in the 2&lt;sup&gt;nd&lt;/sup&gt; round of the &lt;a href="http://community.topcoder.com/tco11/algorithm/"&gt;TCO 2011&lt;/a&gt;. This was the round you had to get past to get a t-shirt.&lt;br /&gt;&lt;br /&gt;So what happened? One thing I like to do with problems is work out a couple simple inputs by hand to make sure I understand the problem. As one of my former co-workers liked to say, &lt;a href="http://www.anvari.org/fortune/Sarcasm/18_if-you-dont-know-how-to-do-something-you-dont-know-how-to-do-it-with-a-computer-anonymou.html"&gt;if you don't know how to do something, you don't know how to do it with a computer&lt;/a&gt;. On the first problem from Saturday's contest, the input was a positive integer, and we had to return an integer based on the problem description. I worked out the answers for the inputs 1 - 6. Unfortunately, I had come up with the wrong answer for an input of 6. Since 6 wasn't one of the sample inputs, I didn't know I was wrong.&lt;br /&gt;&lt;br /&gt;Not surprisingly, trying to generalize from my wrong solution to a general solution didn't work so well. I eventually gave up, but not in time to code up a correct solution to the next problem.&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;walk away from the problem&lt;/span&gt;This is a problem that I've had before, and I've seen it in others. Once you make a mistake, it can be really hard to see that mistake. Your mind tends to &lt;a href="http://madkingsmusings.blogspot.com/2007/03/no-really-dont-get-stuck-in-rut.html"&gt;stay in that rut&lt;/a&gt;&amp;nbsp;and you repeat the mistake when you redo the problem. In the real world (i.e. not the artificially constrained world of programming contests), there are a couple of ways to solvie this type of mistake. The first is to &lt;a href="http://madkingsmusings.blogspot.com/2010/10/tell-it-to-bear.html"&gt;talk through the problem with an external party&lt;/a&gt;. If you can, though, don't explain what you did, but rather let them solve it themselves. That way you don't lead their thought process into the same rut as your own. The second approach is to walk away from the problem. Often times, just walking away from a problem is enough to let you see the solution when you pick it up the next day.&lt;br /&gt;&lt;br /&gt;Unfortunately, neither of those were options for me with the programming contest. So, instead, I get to kick myself for a year. Oh well. Maybe next year will go better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-5787458583994313384?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/5787458583994313384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=5787458583994313384' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5787458583994313384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5787458583994313384'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/06/no-t-shirt-for-me.html' title='No T-Shirt For Me'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-8404478972084283419</id><published>2011-06-20T09:00:00.001-04:00</published><updated>2011-06-20T09:00:11.969-04:00</updated><title type='text'>Testing in Production</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-32-02-metablogapi/7317.image_5F00_0F65063B.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="320" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-32-02-metablogapi/7317.image_5F00_0F65063B.png" width="256" /&gt;&lt;/a&gt;&lt;/div&gt;A co-worker of mine has the picture to the right posted on her door. Last week we put that into practice. We deployed a new internal app which depended on two other components which were also new. On Thursday at 4:30 PM an email went out to tell people they could use it. By 4:45 our server had crashed. Thursday was a late night, but we eventually got the system where it could run without crashing the server. On Friday morning (when it really got used), we had to scramble to fix some less obvious (at least to the end user) bugs, but just as critical.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;So what happened?&lt;/span&gt;&lt;br /&gt;The short answer is, we didn't do enough testing. Every bug that we've found (so far), arguably should've been found in testing. All in all, it was a pretty embarrassing experience. Especially since part of it was our first internal Rails app which I had pushed hard to get us to use. However, in hind-sight if I had it to do all over again, I think I'd have done the same thing. Why?&lt;br /&gt;&lt;br /&gt;Obviously, it's not good to have such a big failure. Plus, I never like to *have* to work late. However, nothing is in a vacuum. Every decision has to be compared to the alternative. We probably could've found most or all of the bugs with another week of testing. So which was better to the Lab? That we test for another week, with the opportunity cost that two developers can't be working on other projects? Or that we deploy a poorly tested app, give a few users a poor experience, and have a couple people work one long day on a Thursday?&lt;br /&gt;&lt;br /&gt;The fact of the matter is, 15 minutes of real user testing found bugs that would've taken at least a week of developer testing. In terms of people hours, it was much more efficient to get some real use than to continue testing. As Jeff Atwood says, &lt;a href="http://www.codinghorror.com/blog/2009/12/version-1-sucks-but-ship-it-anyway.html"&gt;release your buggy software&lt;/a&gt;. So what was the negative? In this particular situation it was an internal app so there are no customers to lose or investors to turn away. The only negative is that a few people probably think less of me and my division. On the plus side, getting the app in front of people helped define requirements better than any amount of up front analysis. Also, the majority of this code will be reused on future apps, so this testing should allow for future high quality releases, allowing us to regain some of that lost reputation.&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;Releasing finds missing requirements&lt;/span&gt;So what's the take home message? Well, I am still embarrassed by how many problems we had with this release. However, given that there were no real negative consequences, it was probably in everyone's best interest for us to release (potentially) buggy software, rather than delay it for more exhaustive testing. Releasing finds missing requirements and shows how the code is really used. No amount of testing can do that.&lt;br /&gt;&lt;br /&gt;In general, I try to have this blog be a voice for higher quality software, and to not subject users to a bad experience. However, at times it might be better to give a temporary bad experience so that the majority can have a good experience sooner.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-8404478972084283419?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/8404478972084283419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=8404478972084283419' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/8404478972084283419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/8404478972084283419'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/06/testing-in-production.html' title='Testing in Production'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-8595715839025538758</id><published>2011-06-14T09:00:00.029-04:00</published><updated>2011-06-14T09:00:07.732-04:00</updated><title type='text'>Too Slow</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-sqrHicya1yQ/Tfa4sQ3t_YI/AAAAAAAAAsk/9_B8XC4S-Fo/s1600/me_surf.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="191" src="http://3.bp.blogspot.com/-sqrHicya1yQ/Tfa4sQ3t_YI/AAAAAAAAAsk/9_B8XC4S-Fo/s400/me_surf.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;I have been remiss in getting my posts out. A few weeks ago I was at &lt;a href="http://2011.icse-conferences.org/"&gt;ICSE&lt;/a&gt;. I had intended on making a post based on that conference, but as soon as it was over, I went into vacation mode. &lt;a href="http://www.hawaiistateparks.org/parks/oahu/index.cfm?park_id=15"&gt;While on vacation&lt;/a&gt; &lt;a href="http://www.govisithawaii.com/2009/01/09/kayaking-the-mokulua-islands-with-kailua-sailboards-kayaks/"&gt;I found&lt;/a&gt; &lt;a href="http://www.best-of-oahu.com/lanikai-pillbox-hike.html"&gt;a number&lt;/a&gt; of &lt;a href="http://www.bishopmuseum.org/"&gt;things to do&lt;/a&gt; that &lt;a href="http://www.sunsetsurattsurfschool.com/"&gt;kept me away&lt;/a&gt; from computers. About the only computer thing I did do was compete in the &lt;a href="http://code.google.com/codejam/contest/dashboard?c=1150486"&gt;2&lt;sup&gt;nd&lt;/sup&gt; round of the Google Code Jam&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This was more challenging than normal for me because Google tries to schedule their contests to be at a somewhat reasonable hour for most of the world. They've made the decision that there is this &lt;a href="http://maps.google.com/?ie=UTF8&amp;amp;ll=-2.635789,-165.058594&amp;amp;spn=91.105943,109.160156&amp;amp;z=3"&gt;great big ocean&lt;/a&gt;&amp;nbsp;without a lot of people living in it, and so they schedule it so the contest is in the middle of the night for the Pacific. Of course, I happened to be in Hawai'i at this time and so got up at O'dark-thirty for the 4AM contest start. Surprisingly, I not only did well enough to get a t-shirt (top 1000), but did &lt;a href="http://code.google.com/codejam/contest/scoreboard?c=1150486#sp=321"&gt;well enough to advance&lt;/a&gt; to the next round (top 500).&lt;br /&gt;&lt;br /&gt;So this is why I missed the last two posts. What about this post? Well, after two weeks away, we had a lot of catching up to do at home and at work. The little bit of free computer time I was able to find went to practicing for the &lt;a href="http://code.google.com/codejam/contest/dashboard?c=1158485"&gt;3&lt;sup&gt;rd&lt;/sup&gt; round of the Google Code Jam&lt;/a&gt;&amp;nbsp;where they would select the top 25. Unfortunately, that was for naught, as &lt;a href="http://code.google.com/codejam/contest/scoreboard?c=1158485#vt=1&amp;amp;sp=261"&gt;I did not advance&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Too Slow&lt;/span&gt;&lt;br /&gt;24 out of 25 of the advancers solved the first 3 problems, as well as the small input set on the 4&lt;sup&gt;th&lt;/sup&gt; problem. I, also, know how to solve the first 3 problems as well as the small input set on the 4&lt;sup&gt;th&lt;/sup&gt; problem. So what's the difference between the advancers and me? On this problem set, speed. At the same time as I was submitting my solution to the second problem, 25&lt;sup&gt;th&lt;/sup&gt; place was submitting his final solution.&lt;br /&gt;&lt;br /&gt;The top competitors were probably a little faster at coming up with algorithms and at coding the algorithms. However, what killed me was debugging. In the first problem, I had numerous bugs in my code that I had to spend time tracking down. Careless mistakes on this problem singlehandedly&amp;nbsp;knocked me out of the running to advance. On the third problem, bugs were again my downfall. I didn't take my &lt;a href="http://madkingsmusings.blogspot.com/2011/04/programming-contest-season.html"&gt;own advice&lt;/a&gt;&amp;nbsp;and I didn't spend a couple minutes thinking about how to best structure my data structures. I just used the first thing that popped into my head which ended up being very error prone. After the contest, when I had a couple minutes to think without the pressure, I came up with a much simpler way to code the same algorithm.&lt;br /&gt;&lt;br /&gt;So what's the moral of this story? Don't make mistakes! :) And trying to do things in a rush just makes it more likely that you will make mistakes. Oh, and going on vacation during a contest is probably not conducive to preparing.&lt;br /&gt;&lt;br /&gt;As for why this post is a day late... well, I am just a little bit slow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-8595715839025538758?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/8595715839025538758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=8595715839025538758' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/8595715839025538758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/8595715839025538758'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/06/too-slow.html' title='Too Slow'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-sqrHicya1yQ/Tfa4sQ3t_YI/AAAAAAAAAsk/9_B8XC4S-Fo/s72-c/me_surf.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-2183372421960726522</id><published>2011-05-23T09:00:00.002-04:00</published><updated>2011-05-23T09:00:12.567-04:00</updated><title type='text'>Moore's Law Doesn't Catch Imagination</title><content type='html'>Back in 1992 I had an opportunity to use an &lt;a href="http://en.wikipedia.org/wiki/Intel_iPSC/860"&gt;iPSC super computer&lt;/a&gt;. If I recall correctly, the particular computer I used had a total of 192 MB of RAM. This was just an outrageously huge amount. It was enough that I didn't really pay too much attention to the memory I was using on the program I was writing. When it was suggested that maybe my program was failing due to using too much memory, I was incredulous. But upon further inspection of my program, sure enough, I was trying to allocate arrays requiring a few hundred megabytes of memory.&lt;br /&gt;&lt;br /&gt;Using a generalization of &lt;a href="http://en.wikipedia.org/wiki/Moore's_law"&gt;Moore's Law&lt;/a&gt;&amp;nbsp;that says that processing power should double every 18 months (I know, that's not the real law), and given that approximately 12 18month periods have passed since then, computers should be 4,000 times as powerful. And sure enough, you can find &lt;a href="http://en.wikipedia.org/wiki/Watson_(computer)"&gt;supercomputers with terabytes of RAM&lt;/a&gt;, and the &lt;a href="http://madkingsmusings.blogspot.com/2011/03/visiting-foreign-land.html"&gt;laptop I just bought&lt;/a&gt;&amp;nbsp;has 8GB of RAM, which is 4,000 times what was &lt;a href="http://www.zdnet.com/blog/perlow/1991s-pc-technology-was-unbelievable/16413"&gt;common then&lt;/a&gt;. I am actually kind of amazed at how accurate this rule of thumb is.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;a href="http://en.wikipedia.org/wiki/Spinning_wait_cursor" imageanchor="1" style="border-style: none; border: none; clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="100" src="http://4.bp.blogspot.com/-pqJvZUsy3tk/TcG-1AaV35I/AAAAAAAAAsg/cXTnsYu-PkI/s200/beachball2.png" width="100" /&gt;&lt;/a&gt;However, my programming goals apparently grow just as fast. Just the other day, as I was &lt;a href="http://madkingsmusings.blogspot.com/2011/04/programming-contest-season.html"&gt;practicing for programming contests&lt;/a&gt;, I tried to allocate a 4 terabyte array. Needless to say, my program failed. And for some reason, instead of just crashing, it ground my computer to a halt, eventually requiring a reboot. I suspect in another two decades I'll be crashing programs by trying to allocate exabyte arrays.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;For many business or web apps, network or database latency is going to be your biggest bottleneck. Because of this we often treat memory as infinite and processing time as zero. The emphasis on writing readable, maintainable code as opposed to the most efficient is typically the right trade-off. However, memory isn't infinite, and processing time isn't zero. Before coding a module that requires pulling your entire database into memory, think about whether this is a couple megabytes of memory or a hundreds of terabytes. &amp;nbsp;Modern computers are powerful, but they still aren't as powerful as we'd all like.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-2183372421960726522?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/2183372421960726522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=2183372421960726522' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2183372421960726522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2183372421960726522'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/05/moores-law-doesnt-catch-imagination.html' title='Moore&apos;s Law Doesn&apos;t Catch Imagination'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-pqJvZUsy3tk/TcG-1AaV35I/AAAAAAAAAsg/cXTnsYu-PkI/s72-c/beachball2.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6197427364189704050</id><published>2011-05-16T09:00:00.010-04:00</published><updated>2011-05-16T09:00:10.766-04:00</updated><title type='text'>Rails form_for_hash</title><content type='html'>Ruby on Rails has some nice &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html"&gt;form building helpers&lt;/a&gt;&amp;nbsp;if you are building an HTML form for populating an &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&gt;ActiveRecord&lt;/a&gt;&amp;nbsp;model. But what if you want your front end forms to not exactly map to your back end models? Maybe you are presenting a logical view that corresponds to parts of different models. Well, Rails still provides &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html"&gt;helpers&lt;/a&gt;&amp;nbsp;that let you roll your own form. However, now you have to manually handle reading and writing values to the form. It would be nice if you could get the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;form_for&lt;/span&gt; functionality with just a simple &lt;a href="http://ruby-doc.org/core/classes/Hash.html"&gt;Hash&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Well, it turns out that with a little bit of work, you can. Based on an idea I found at &lt;a href="http://pullmonkey.com/2008/1/6/convert-a-ruby-hash-into-a-class-object/"&gt;http://pullmonkey.com/2008/1/6/convert-a-ruby-hash-into-a-class-object/&lt;/a&gt;, I created a simple Object which wraps a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Hash&lt;/span&gt; and behaves like an &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ActiveRecord&lt;/span&gt;. This class looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; HashObject&lt;br /&gt;&lt;div class="p1"&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; initialize&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;hash&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;@hash &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; hash&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; method_missing&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;sym, &lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;args, &amp;amp;block&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;@hash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;sym&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;Then to make it easy to use, I put a helper function in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;helper/application_helper.rb&lt;/span&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; form_for_hash&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;hash, name, url, html_options&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;, &amp;amp;&lt;span style="color: #e66170; font-weight: bold;"&gt;proc&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;div class="p1"&gt;&amp;nbsp;&amp;nbsp;object &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; HashObject&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;hash&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;form_for object, :as&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;name, :url&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;url, :html&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;html_options, &amp;amp;&lt;span style="color: #e66170; font-weight: bold;"&gt;proc&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;Now I can use it in the view as such: &amp;nbsp;(e.g. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;new.html.haml&lt;/span&gt;)&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; form_for_hash @person, :person, special_people_path &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :first_name, &lt;span style="color: #00c4c4;"&gt;'First Name'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :first_name&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :last_name, &lt;span style="color: #00c4c4;"&gt;'Last Name'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :last_name&lt;br /&gt;&amp;nbsp;&amp;nbsp;%br&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :phone_number, &lt;span style="color: #00c4c4;"&gt;'Phone Number'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :phone_number&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt;&lt;span style="color: #9999a9;"&gt;# etc etc etc&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Then in your SpecialPeopleController you would have methods like&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; new&lt;br /&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp;@person &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;:first_name&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;'Jenny'&lt;/span&gt;, :phone_number&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;'867-5309'&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; create&lt;br /&gt;&amp;nbsp;&amp;nbsp;@person &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:person&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #9999a9;"&gt;# logic for populating/updating models based on hash goes here&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;And that is all you need to do to be able to use a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Hash&lt;/span&gt; to populate your forms.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6197427364189704050?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6197427364189704050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6197427364189704050' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6197427364189704050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6197427364189704050'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/05/rails-formforhash.html' title='Rails form_for_hash'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-3662664869679188765</id><published>2011-05-09T09:00:00.054-04:00</published><updated>2011-05-09T09:04:34.271-04:00</updated><title type='text'>Programming Contests and Languages</title><content type='html'>This past weekend was the qualifying round for the &lt;a href="http://code.google.com/codejam/"&gt;Google Code Jam&lt;/a&gt;. Google allows you to use any language you would like in this contest. This year I have decided to use &lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt;&amp;nbsp;as my language of choice. In the past I have always used Java, as that is the language that I currently know the best. However, I think Ruby will allow me to write cleaner, more compact code which will let me code faster and hopefully write fewer bugs.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Simplicity&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Why do I think this? Let me give you a simple example. A common task in these contests is to be able to read in a line of space separated integers and convert it into an array for later processing. Here is how I accomplish this task in Java:&lt;/div&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; BufferedReader reader = &lt;span style="color: blue;"&gt;new&lt;/span&gt; BufferedReader(&lt;span style="color: blue;"&gt;new&lt;/span&gt; InputStreamReader(System.in));&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; String[] parts = reader.readLine().split(&lt;span style="color: maroon;"&gt;" "&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt;[] array = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt;[parts.length]; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; i = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; i &amp;lt; array.length; i++) {&lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;   array[i] = Integer.parseInt(parts[i]); &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; }&lt;/pre&gt;&lt;div&gt;Here is how I do the same thing in Ruby:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;array &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;gets&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;split&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;map &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; |i| i&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_i&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Bugs&lt;/span&gt;&lt;br /&gt;Imagine I now want to loop through the array and determine how many array elements are the same as their position. I.e. if I have the array &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;[0, 2, 1, 3, 5, 4]&lt;/span&gt;, elements 0 and 3 are the same as their index and so I'd want to get a value of 2. Here is Java code that accomplishes this task:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt; count = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;;&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; i = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; i &amp;lt; array.length; i++) {&lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   &lt;span style="color: blue;"&gt;if&lt;/span&gt; (array[i] == i) count++; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; }&lt;/pre&gt;Here's Ruby code that does the same thing:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;count &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;br /&gt;array&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;each_index &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;|i| count &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; array&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;i&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; i &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Now these two code samples are almost identical. Some people would argue that its just a matter of style which you prefer. However, as someone who has written countless loops like this under the time pressure of programming contests I can tell you there is one crucial difference. In the Java code I am explicitly incrementing the index variable &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;i&lt;/span&gt; in the for loop and checking it against the length of the array. In Ruby, the method &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;each_index&lt;/span&gt; does this for me. Why does this matter? Well, imagine that you have nested loops. Well, it can be really easy to accidentally write your inner loop like:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; j = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; j &amp;lt; array2.length; i++)&lt;/pre&gt;&lt;/div&gt;and now you suddenly have an infinite loop. More insidious, you could write:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt;[][] map = &lt;span style="color: blue;"&gt;new&lt;/span&gt; &lt;span style="color: blue;"&gt;int&lt;/span&gt;[ROWS][COLS]; &lt;br /&gt;&lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; r = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; r &amp;lt; map.length; r++) &lt;br /&gt;   &lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; c = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; c &amp;lt; map.length; c++)&lt;br /&gt;     array[r][c] = r + c;&lt;/pre&gt;when you meant to write the column loop:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;for&lt;/span&gt; (&lt;span style="color: blue;"&gt;int&lt;/span&gt; c = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; c &amp;lt; map[r].length; c++)&lt;br /&gt;&lt;/pre&gt;and now you have code that works whenever &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ROWS == COLS&lt;/span&gt;, but not otherwise.&lt;br /&gt;&lt;br /&gt;Having had many conversations with other competitors at contests, I can tell you that I am not the only one who has wasted large amounts of time tracking down dumb bugs just like these. Nor am I the only one who has unknowingly submitted a buggy solution because I only tested on square maps, and so failed a problem because of such a silly bug. My hope is that by using Ruby I will spend less time dealing with bugs like this, allowing me more time to actually solve the problem at hand.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Performance&lt;/span&gt;&lt;br /&gt;Here's the catch. The dynamic aspect of Ruby comes at a cost. Many problems in programming contests like the Google Code Jam are very computationally intensive and there is always a time limit for how long your code can run. In fact &lt;a href="http://code.google.com/codejam/faq.html"&gt;GCJ's FAQ&lt;/a&gt;&amp;nbsp;warns that Python's slowness may cause problems for you on some problems. My assumption is that Ruby has similar performance characteristics to Python, so I decided to do some simple comparison's between Ruby and Java performance on some programming contest type tasks.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Test 1&lt;/span&gt;&lt;br /&gt;As my first test, I came up with a solution for GCJ's &lt;a href="http://code.google.com/codejam/contest/dashboard?c=32017#s=p2"&gt;2008 Round 1B's Problem C. Mousetrap&lt;/a&gt;. My Ruby solution takes about 40 seconds to correctly process Google's large input set. The same algorithm in Java takes about 4 seconds to process the same data set.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Test 2&lt;/span&gt;&lt;br /&gt;My second test was based on a suggestion of a friend who was inspired by &lt;a href="http://code.google.com/codejam/contest/dashboard?c=975485#s=p3"&gt;Problem D. GoroSort&lt;/a&gt;&amp;nbsp;on this year's qualification round. What I did was write a program which creates every permutation of N integers, and then for each permutation counts up how many numbers are the same as their index. In addition to Java and Ruby, I dusted off my C and wrote a C implementation, as well, for comparison. Here are the results I get on my &lt;a href="http://madkingsmusings.blogspot.com/2011/03/visiting-foreign-land.html"&gt;Macbook Pro&lt;/a&gt;. (all values are in seconds)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&amp;nbsp;N &amp;nbsp;&amp;nbsp;&lt;/th&gt;&lt;th&gt;&amp;nbsp; Ruby &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/th&gt;&lt;th&gt;&amp;nbsp;Java &amp;nbsp; &amp;nbsp;&lt;/th&gt;&lt;th&gt;C&amp;nbsp;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp; 6&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.015&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.20&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.003&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp; 7&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.028&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.21&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.004&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp; 8&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.15&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.22&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.007&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp; 9&lt;/td&gt;&lt;td&gt;&amp;nbsp;1.3&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.24&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.021&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;10&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;14&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.55&lt;/td&gt;&lt;td&gt;&amp;nbsp;0.16&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;11&lt;/td&gt;&lt;td&gt;&amp;nbsp;160&lt;/td&gt;&lt;td&gt;&amp;nbsp;3.9&lt;/td&gt;&lt;td&gt;&amp;nbsp;2.4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;12&lt;/td&gt;&lt;td&gt;&amp;nbsp;1,980&amp;nbsp;&lt;/td&gt;&lt;td&gt;&amp;nbsp;43&lt;/td&gt;&lt;td&gt;&amp;nbsp;36&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/center&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;There is a definite and meaningful difference in performance between Ruby and the other languages for larger data sets. Still, for the majority of problems Ruby will be fast enough. I will just need to make sure I do a &lt;a href="http://en.wikipedia.org/wiki/Big_O_notation"&gt;big-O&lt;/a&gt;&amp;nbsp;analysis of my algorithms before I implement them, and if there is any doubt as to performance, write it in Java rather than Ruby. For any problems where speed doesn't look to be an issue, I think I will try to use Ruby. Hopefully this will work for me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-3662664869679188765?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/3662664869679188765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=3662664869679188765' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/3662664869679188765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/3662664869679188765'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/05/programming-contests-and-languages.html' title='Programming Contests and Languages'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-4533420840225796554</id><published>2011-05-02T09:00:00.021-04:00</published><updated>2011-05-02T09:00:00.619-04:00</updated><title type='text'>Session Timeouts on Rails</title><content type='html'>Session Timeouts are a perfect example of the conflict between usability and security. From a security standpoint you don't want to leave a user logged in for an extended time period, because someone else may get access to their browser and then act as that person. As an example I have a a Facebook friend who frequently has posts made in her name by friends who get a hold of her phone. On the other hand, as a user, it is really frustrating when I have to keep logging back into a site.&lt;br /&gt;&lt;br /&gt;In general, be only as secure as you need to be. I am ok with my bank logging me out after a short period of inactivity. However, I am willing to risk the occasional prank post on Facebook to not have to keep logging back in. As long as the social sites require a re-entering of a password before allowing a password change or any other personal information changes, they are secure enough from my point of view.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Session Expiration Tasks&lt;/span&gt;&lt;br /&gt;If you want to expire sessions, you need to do a couple of things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Keep track of the time of the user's last action&lt;/li&gt;&lt;li&gt;Show the timeout on the client side, by having a javascript function running on the browser which will load the login page after the session has timed out&lt;/li&gt;&lt;li&gt;Enforce the timeout on the server side by having the server redirect to a login page if a different page is requested after a timeout&lt;/li&gt;&lt;li&gt;Make sure the redirect also happens on Ajax updates&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Rail Implementation&lt;/span&gt;&lt;br /&gt;For purposes of this code, I am assuming that you have a login system like shown in Michael Hartl's "&lt;a href="http://ruby.railstutorial.org/chapters/sign-in-sign-out#top"&gt;Ruby on Rails Tutorial&lt;/a&gt;".&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Tracking Timeout&lt;/span&gt;&lt;br /&gt;&amp;nbsp;Following the example I found at&amp;nbsp;&lt;a href="http://snippets.dzone.com/posts/show/7400"&gt;http://snippets.dzone.com/posts/show/7400&lt;/a&gt;&amp;nbsp;I made my ApplicationController look like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; ApplicationController &amp;lt; ActionController::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp;protect_from_forgery&lt;br /&gt;&amp;nbsp;&amp;nbsp;before_filter :session_expiry&lt;br /&gt;&amp;nbsp;&amp;nbsp;before_filter :update_activity_time&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;include SessionsHelper&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; session_expiry&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;get_session_time_left&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;unless&lt;/span&gt; @session_time_left &amp;gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;sign_out&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;deny_access &lt;span style="color: #00c4c4;"&gt;'Your session has timed out. Please log back in.'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; update_activity_time&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;session&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:expires_at&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #009f00;"&gt;30.&lt;/span&gt;minutes&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;from_now&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; get_session_time_left&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;expire_time &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; session&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:expires_at&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; || Time&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;now&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;@session_time_left &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;expire_time &lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; Time&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;now&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_i&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;In this code we store an expiration time in the session scope, and we make sure the session hasn't expired before performing any actions. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update_activity_time&lt;/span&gt; accomplishes the goal of tracking the user's last action and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;session_expiry&lt;/span&gt; enforces the timeout.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Support Ajax Calls&lt;/span&gt;&lt;br /&gt;A complication comes up if your web application has Ajax calls. If an Ajax request comes in after the session times out you want to do a full page redirect to the login page, not just a partial update. To accomplish this I modified the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;deny_access&lt;/span&gt; method in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;SessionsHelper&lt;/span&gt; module to look like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; deny_access&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;msg &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;nil&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;msg ||&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"Please sign in to access this page."&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;flash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:notice&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; ||&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; msg&lt;br /&gt;&amp;nbsp;&amp;nbsp;respond_to &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;|&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;html &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;store_location&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;redirect_to signin_url&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;js &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;store_location request&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;referer&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;render &lt;span style="color: #00c4c4;"&gt;'sessions/redirect_to_login'&lt;/span&gt;, :layout&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;As you can see, on an Ajax call we store the referrer, i.e. the page that the user was on when they made the Ajax call, and then render a javascript fragment. The file '&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/sessions/redirect_to_login.js.haml&lt;/span&gt;' just consists of the one line of javascript:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;window&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;location&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;replace&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;'#{escape_javascript signin_url}'&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;;&lt;br /&gt;&lt;/pre&gt;which tells the browser to redirect to the login page.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Client Side Timeout&lt;/span&gt;&lt;br /&gt;It can be a frustrating user experience to perform an action on a page, only to find out that you have timed out. Since we've already decided that we will timeout the user, we can't prevent that behavior, but we can at least let the user know they have timed out by automatically redirecting them to the login page when they time out. To accomplish this we add the following javascript lines to our &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public/javascripts/application.js&lt;/span&gt; file&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;function&lt;/span&gt; checkSessionTimeout&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;url&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;$&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;getScript&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;url&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;function&lt;/span&gt; setSessionTimeout&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;url&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; seconds&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;setTimeout&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"checkSessionTimeout(&lt;/span&gt;&lt;span style="color: teal;"&gt;\'&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt; url &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"&lt;/span&gt;&lt;span style="color: teal;"&gt;\'&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;)"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; seconds&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1000&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;+&lt;/span&gt; &lt;span style="color: #008c00;"&gt;15&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;checkSessionTimeout&lt;/span&gt; is just a method which makes an Ajax call (using &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;) to a URL and executes the returned javascript. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;setSessionTimeout&lt;/span&gt; sets a javascript timeout to call &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;checkSessionTimeout&lt;/span&gt; after the specified delay.&lt;br /&gt;&lt;br /&gt;To call these functions we add the following to our &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/layout/application.html.haml&lt;/span&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; @session_time_left&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #b060b0;"&gt;:&lt;/span&gt;javascript&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;$&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;function&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;setSessionTimeout&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'#{check_session_alive_sessions_path}'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; #&lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;@session_time_left&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #b060b0;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Basically, if the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@session_time_left&lt;/span&gt; variable is set, we use the &lt;a href="http://jquery.com/"&gt;jquery&lt;/a&gt; functionality to call the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;setSessionTimeout&lt;/span&gt; method when the page has been loaded. We pass the url that corresponds to the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;check_session_alive&lt;/span&gt; action on the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sessions&lt;/span&gt; controller which looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; check_session_alive&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;get_session_time_left&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; @session_time_left &amp;gt; &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;render &lt;span style="color: #00c4c4;"&gt;'sessions/check_session_alive'&lt;/span&gt;, :layout&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;false&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;sign_out&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;deny_access &lt;span style="color: #00c4c4;"&gt;'Your session has timed out. Please log back in.'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;If the session has timed out, we just call &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;deny_access&lt;/span&gt; which we showed above and already handles the redirect. If the session hasn't timed out, we render &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sessions/check_session_alive.js.haml&lt;/span&gt; which just calls &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;setSessionTimeout&lt;/span&gt; again, with the new timeout value:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;setSessionTimeout&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'#{check_session_alive_sessions_path}'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;,&lt;/span&gt; #&lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;@session_time_left&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;One Catch&lt;/span&gt;&lt;br /&gt;We don't want to enforce that the session is valid on the session actions like login, logout, and check_session_alive. To fix this we add the following to the top of our SessionsController:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; SessionsController &amp;lt; ApplicationController&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;skip_before_filter :session_expiry&lt;br /&gt;&amp;nbsp;&amp;nbsp;skip_before_filter :update_activity_time&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;With that you have a bare bones session expiration system on your Rails app. I will leave it as an exercise to the reader to add extra features, like giving a user a 5 minute warning that they are about to be logged out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-4533420840225796554?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/4533420840225796554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=4533420840225796554' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4533420840225796554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4533420840225796554'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/05/session-timeouts-on-rails.html' title='Session Timeouts on Rails'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-7255170194528701600</id><published>2011-04-25T09:00:00.016-04:00</published><updated>2011-04-25T09:00:10.378-04:00</updated><title type='text'>Programming Contest Season</title><content type='html'>It is the time of year for programing contests. &lt;a href="http://code.google.com/codejam/"&gt;Google Code Jam&lt;/a&gt; has its qualifying round on May 6&lt;sup&gt;th&lt;/sup&gt; and the &lt;a href="http://community.topcoder.com/tco11/algorithm/"&gt;TopCoder Open&lt;/a&gt; starts the qualifiers for their algorithm contest on May 14&lt;sup&gt;th&lt;/sup&gt;. Assuming nothing unexpected comes up, I intend to participate in both of these contests again. If I am to have any hope of doing well in either of the contests I will have to prepare.&lt;br /&gt;&lt;br /&gt;There are 3 main skills you need to have to able to do well in these type of contests.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Problem Solving&lt;/span&gt;&lt;br /&gt;These contests are not software development contests. You don't need to be able to build an application better than other people, or even at all. What you need to do is be able to solve puzzles. Sometimes this is just a simple matter of following the instructions in the problem statement. More often it is a matter of realizing that if you look at the problem the right way, you can map it to another problem (e.g. graph traversal, binary search, etc.) that you already know how to solve. And occasionally you'll have to come up with a completely novel solution on the spot.&lt;br /&gt;&lt;br /&gt;There are a few things you can do to get better at this. The first is to learn the known solutions to common computer science problems. It doesn't do you any good to recognize that a problem requires finding the shortest path between two nodes in a graph, if you don't know an algorithm that accomplishes that. The second is to practice recognizing places where these algorithms apply. Practice solving problems helps with this. Seeing how other people solved the same problem can also help.&lt;br /&gt;&lt;br /&gt;The more knowledge you have of existing algorithms and the more experience you have at recognizing the use of these algorithms, the less often you'll come across a truly novel problem. And even when you do, you'll be better prepared to find a solution.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Programming&lt;/span&gt;&lt;br /&gt;The problems posed in these contests don't require a lot of software engineering. It is rare for a solution to run as long as 100 lines, even in a relatively verbose language like Java. However, it is still a key skill. If you can't convert an algorithm into code accurately and quickly, you won't last long in a contest. When learning algorithms, don't just read about them in a book or online, code them up. And then code them again a different way. That experience will be helpful when coding under the gun.&lt;br /&gt;&lt;br /&gt;Besides being able to code algorithms, you need to know your language. Do you know how to format strings, parse strings, and round numbers? Do you know the collection libraries provided and can you use them without having to look up API documentation? Do you know the performance characteristics of various features like autoboxing or string manipulation? How long will it take you to execute an n&lt;sup&gt;4&lt;/sup&gt; algorithm when n = 50? Does it matter what calculations you perform in the inner loop? Can you create an array of length one million? Ten million? One hundred million? How big of a value can you store in an integer? a long? What happens on overflow? Are you familiar with the precision issues that can crop up when using floating point numbers?&lt;br /&gt;&lt;br /&gt;If you don't know the answers to the above questions, learn them. Not just from a book, but by writing programs. Knowing what is feasible and what isn't can help you find a workable algorithm for solving a problem.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Speed&lt;/span&gt;&lt;br /&gt;These contests are timed. It doesn't do you any good if you can solve a problem, but it takes 6 hours. The only way that I know to improve speed is to practice, practice, practice. There are a couple of hints I can give you, though.&lt;br /&gt;&lt;br /&gt;The first is that bugs are the enemy. Nothing kills your time more than having to debug a program. There are things you can do to reduce bugs. One is to be consistant. Be consistant with how you name variables and types, where and when you use curly braces and how you write algorithms. Another is to use as much structure as you need. Even though it is quicker to type one letter variable names and to create one long function, if meaningful variable names and separate functions and/or classes helps structure your code in a way that you can understand it, the time lost in typing will be more than made up in debugging.&lt;br /&gt;&lt;br /&gt;&lt;span class="pullout"&gt;Take your time.&lt;/span&gt;The last, but most important piece of advice in being quick is to take your time. Plan your solution. If it is complicated, sketch out your solution on paper and pencil before coding. Spend a couple minutes analyzing the performance characteristics of your solution. A couple minutes of thinking can save 20 wasted minutes of programming a flawed solution. Taking a couple extra minutes coding up a solution carefully and cleanly can save hours of debugging time. (and the contests don't last hours, so that is critical).&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Out Coding&lt;/span&gt;&lt;br /&gt;Well, I guess I should stop procrastinating and get to practicing so that I have a hope of advancing in these contests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-7255170194528701600?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/7255170194528701600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=7255170194528701600' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7255170194528701600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7255170194528701600'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/04/programming-contest-season.html' title='Programming Contest Season'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-1122044669952274323</id><published>2011-04-18T09:00:00.001-04:00</published><updated>2011-04-18T09:00:05.741-04:00</updated><title type='text'>attr_accessible and Security in Rails</title><content type='html'>This is a story of how easy it can be to&amp;nbsp;inadvertently&amp;nbsp;put security flaws in your programs.&lt;br /&gt;&lt;br /&gt;Given that I had implemented &lt;a href="http://madkingsmusings.blogspot.com/2011/03/google-openid-via-rails.html"&gt;authentication&lt;/a&gt;&amp;nbsp;and &lt;a href="http://madkingsmusings.blogspot.com/2011/04/role-based-authorization-in-rails.html"&gt;authorization&lt;/a&gt;, I wanted a place where users could edit their profile information. However, I wanted to always keep the user's original name and email address that was provided by OpenID authentication. So my thought was that I would provide the user with a "preferred email address" and a "display name" which I would pre-populate based on what OpenID provided, but which they could change to anything they wanted. I'd still keep the original information, but just not ever show it to them.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Step One - New Database Columns&lt;/span&gt;&lt;br /&gt;This was just a matter of creating a new migration:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; AddDisplayNameToUser &amp;lt; ActiveRecord::Migration&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; self&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;up&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;add_column :users, :display_name, :string&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;add_column :users, :preferred_email, :string&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; self&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;down&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;remove_column :users, :display_name&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;remove_column :users, :preferred_email&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;and running&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;rake db:migrate&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Step 2 - Create Default Values in Model&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Pre-populating these new columns was easy, all I needed was to use the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;after_initialize&lt;/span&gt;" hook in ActiveRecord. To do this, I added the following lines to my User model:&lt;/div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;after_initialize :init&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; init&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;display_name ||&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"#{self.last_name}, #{self.first_name}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;preferred_email ||&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;email&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Step 3 - Create Web Pages&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I created a Profile resource with the following line in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;routes.rb&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;resource :profile, :only&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:show, :edit, :update&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;In the "show" action, I show the user their display name and preferred email. The edit page allows them to edit these two pieces of information. The HAML for this is:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;%h1 Edit Your Profile&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; form_for @user, :url&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;profile_path &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |f|&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :display_name&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :display_name, :size&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;50&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;%br&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :preferred_email, &lt;span style="color: #00c4c4;"&gt;'Preferred email address'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :preferred_email, :size&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #008c00;"&gt;50&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;%br&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;submit &lt;span style="color: #00c4c4;"&gt;'Update Profile'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The update method in the controller, which gets called when the form is submitted looks like:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; update&lt;br /&gt;&amp;nbsp;&amp;nbsp;current_user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;update_attributes params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:user&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;redirect_to profile_path&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;And with that, I have a page where a user can update their own information. I'm done, right?&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Security Flaw&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Do you see the security flaw?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem is one that frequently comes up in client/server settings, particularly on the web. The server is putting too much trust in the client. It's all well and good to restrict the user's edit page to just the Display Name and Preferred Email fields, but a wily user will just bypass the web browser and directly make an HTTP request with parameters set to change fields that we don't want changed, like the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;:identifier_url&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So how do we fix this? Well the obvious way is to replace the line&lt;/div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;current_user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;update_attributes params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:user&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update&lt;/span&gt; method with code that explicitly just sets the fields that we care about. But then we lose the convenience of letting this Rails library method do the work for us. Luckily there's a solution, and I am sure the title of this post has not given you any hint to that solution. If you add the line:&lt;/div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;attr_accessible :display_name, :preferred_email&lt;br /&gt;&lt;/pre&gt;to your User model class, you are telling Rails to allow those fields to be updatable via a mass assign command like &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update_attributes&lt;/span&gt;. More importantly, as soon as you've declared at least one field as &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;attr_accessible&lt;/span&gt;, then all of the non-declared fields can't be update this way.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Take Home Message&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="pullout"&gt;Never Trust the Client&lt;/span&gt;I am sure you know this already, but it bears repeating: Never Trust the Client. There is nothing stopping a malicious user from writing their own client and sending your server any data they want. Unfortunately, this requires eternal vigilance and learning. Sometimes holes, like the one above, exist not because of code you wrote, but because of the code you didn't write. If I hadn't read about this exact situation in Michael Hartl's &lt;a href="http://www.amazon.com/Ruby-Rails-Tutorial-Addison-Wesley-Professional/dp/0321743121/"&gt;Ruby on Rails 3 Tutorial&lt;/a&gt;, I might never have even realized the security hole I had created.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-1122044669952274323?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/1122044669952274323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=1122044669952274323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1122044669952274323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/1122044669952274323'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/04/attraccessible-and-security-in-rails.html' title='attr_accessible and Security in Rails'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-5015707707474917961</id><published>2011-04-11T09:00:00.110-04:00</published><updated>2011-04-11T09:00:08.935-04:00</updated><title type='text'>New Languages Can Broaden Your Horizons</title><content type='html'>&lt;span class="pullout"&gt;"A language that doesn't affect the way you think about programming is not worth knowing." -Alan Perlis&lt;/span&gt;In linguistics there is theory called the&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Linguistic_relativity"&gt;Sapir–Whorf hypothesis&lt;/a&gt;&amp;nbsp;which asserts that our language limits and defines our cognitive processes. In the computer realm &lt;a href="http://en.wikipedia.org/wiki/Alan_Perlis"&gt;Alan Perlis&lt;/a&gt;&amp;nbsp;once said that, "&lt;a href="http://www.cs.yale.edu/quotes.html"&gt;A language that doesn't affect the way you think about programming is not worth knowing.&lt;/a&gt;" &amp;nbsp;I don't know how true these statements are, but I do know that just about every programming language I have learned has taught me new ways to think about software development. In many cases I wasn't even aware of what I didn't know until the new language opened my eyes.&lt;br /&gt;&lt;br /&gt;Here is an overview of languages that have changed my way of thinking about programming, in the order that I used them. I realize that some of the concepts that I learned in one language could've been learned in another, but this is the order I personally learned things. Sometimes just the act of doing something new can show you things that you could have done with the old tools, but hadn't considered in the past.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Languages&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Commodore64.jpg/320px-Commodore64.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Commodore64.jpg/320px-Commodore64.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;BASIC&lt;/b&gt; (in particular,&lt;a href="http://en.wikipedia.org/wiki/Commodore_64"&gt; Commodore 64&lt;/a&gt; BASIC) - my first programming language. While much of what I learned at this time I later had to unlearn, learning that computers aren't magical but just do what the programmer tells them has had lifelong value.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Assembly/Machine Language&lt;/b&gt; - I learned what the computer actually did - i.e. what does high level code get turned into? What instructions can the CPU actually perform?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Pascal&lt;/b&gt; - with Pascal (well, technically &lt;a href="http://en.wikipedia.org/wiki/Karel_(programming_language)"&gt;Karel the Robot&lt;/a&gt;&amp;nbsp;which we did for a month, first) I was introduced to the concepts of functions and procedures, as well as how to use looping constructs effectively. I also learned about pointers and data structures in Pascal. (i.e. the basics of structured programming as we know it). I was also first exposed to separating interfaces from implementations with Pascal (&lt;a href="http://en.wikipedia.org/wiki/Turbo_Pascal#Units"&gt;Turbo Pascal's Units&lt;/a&gt;), but it didn't really take.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;C&lt;/b&gt; - C was a combination of Pascal and Machine Language for me. I still could use all of the structured programming concepts that I knew, but with C you could see how it tied to machine code. e.g. pointers are just memory addresses. They say that C is&amp;nbsp;"&lt;a href="http://en.wikibooks.org/wiki/A_Little_C_Primer"&gt;A language which combines the flexibility of assembly language with the power of assembly language.&lt;/a&gt;" For me this was its beauty. While not being quite at the Assembly level, I could understand exactly what my programs were doing.&amp;nbsp;I also learned about the difference between &lt;a href="http://en.wikipedia.org/wiki/Compile_time"&gt;compile time&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Link_time"&gt;link time&lt;/a&gt;, which is critical to understanding how to build large systems.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;C++&lt;/b&gt; - This was my first real exposure to OO. However, it didn't really click. I liked the encapsulation aspect of grouping methods with data in classes, but at this point in my programming life I still didn't "get" OO. What I mainly got from C++ was a way to structure my C code.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;LISP&lt;/b&gt;&amp;nbsp;- besides having Lots of Irritating Stupid Parenthesis, it introduced me to functional programming. I learned it is possible to program with &lt;a href="http://en.wikipedia.org/wiki/Immutable_object"&gt;variables that don't vary&lt;/a&gt; and without iterative constructs (Lisp has looping constructs, we just didn't use them till later on in the class). Very powerful ideas, though I have to admit that I didn't use LISP for long (even though I thought it was really cool), so I probably didn't absorb as much from it as I could've from the language.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Java&lt;/b&gt; - with Java I finally got OO. (or at least I think I get it now. &amp;nbsp;:) ). I think the key feature that helped me was the concept of an &lt;a href="http://download.oracle.com/javase/tutorial/java/concepts/interface.html"&gt;interface&lt;/a&gt;. Why would you want to create a "class" that doesn't do anything? It was the struggle to answer that question that turned on the&amp;nbsp;light bulb&amp;nbsp;in my head for getting OO. The other cool idea Java introduced me to was the idea of namespaces. While C++ was starting to get them at that time, I had never used them, and had been burned by class name collisions. Namespaces are a good idea for large programs, and a necessary one for large scale code reuse.&lt;br /&gt;&lt;br /&gt;When Java 5 came out and &lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/annotations.html"&gt;annotations&lt;/a&gt; came into the language, this was another feature that didn't seem to have any value at first. I have since seen that there can be pretty large value in &lt;a href="http://en.wikipedia.org/wiki/Declarative_programming"&gt;declarative programming&lt;/a&gt;&amp;nbsp;though I don't think I have fully internalized how to use the power of it yet. (i.e. I can see the power of this idea, but it hasn't actually transformed me yet).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;C#&lt;/b&gt; - when I first learned C#, it didn't change my way of thinking. It was just Java with slightly different syntax. But then I got a chance to play around with &lt;a href="http://msdn.microsoft.com/en-us/library/bb308959.aspx"&gt;LINQ&lt;/a&gt;. There are two powerful ideas that I got out of LINQ. The first is directly using query language notation inside a "normal" programming language. The second is that of late evaluation. i.e. if you had something like&lt;br /&gt;&lt;pre&gt;DataContext context = &lt;span style="color: blue;"&gt;new&lt;/span&gt; DataContext(&amp;lt;db stuff&amp;gt;); &lt;br /&gt;Table&amp;lt;Person&amp;gt; people = context.getTable&amp;lt;Person&amp;gt;(); &lt;br /&gt;var query = people.Where(p =&amp;gt; (p.age &amp;gt;= &lt;span style="color: maroon;"&gt;20&lt;/span&gt; &amp;amp;&amp;amp; p.age &amp;lt;= &lt;span style="color: maroon;"&gt;25&lt;/span&gt;))&lt;br /&gt;                  .OrderBy(p =&amp;gt; p.name).Select(p =&amp;gt; p); &lt;br /&gt;&lt;span style="color: blue;"&gt;foreach&lt;/span&gt; (var person &lt;span style="color: blue;"&gt;in&lt;/span&gt; query) {&lt;br /&gt;  Console.WriteLine(&lt;span style="color: maroon;"&gt;"{0} is {1} years old"&lt;/span&gt;, person.name, person.age) &lt;br /&gt;}&lt;/pre&gt;I would've naively implemented the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;getTable &lt;/span&gt;method to return the entire datatable, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Where &lt;/span&gt;to return the reduced datatable, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;OrderBy &lt;/span&gt;would sort, and then &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Select &lt;/span&gt;would return an array with the values. This is obviously much less efficient then just letting the database do the query for you. One of the clever thing that the LINQ folks did is they accomplished this. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Where&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;OrderBy &lt;/span&gt;and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Select&amp;nbsp;&lt;/span&gt;all return objects describing their actions, not the results of their actions. Its not until you try enumerating the elements in the query in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;foreach &lt;/span&gt;loop that any data is actually retrieved or manipulated. At that point the code is able to take advantage of everything you specified to act efficiently. Compare that to the Unix command line:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;find . -name "*" -print | grep "taxes" | sort -r&lt;/span&gt;&lt;br /&gt;I am used to this type of environment where find does a calculation and passes data to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;grep &lt;/span&gt;which then passes data to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sort&lt;/span&gt;. The idea of just passing metadata around and then doing the calculation at the end requires greater coordination between components, but when you have that the result is improved efficiency.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ruby &lt;/b&gt;- with Ruby I am learning that the static typing I learned to love in Java might not be as important as I believed. &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;Duck typing&lt;/a&gt; is a great idea. Even cooler are ruby blocks, i.e. &lt;a href="http://en.wikipedia.org/wiki/Closure_(computer_science)"&gt;closures&lt;/a&gt;. Being able to pass blocks of code around is HUGE and definitely a transformative idea. Prior to learning Ruby, I would occasionally run into situations where I was frustrated by Java's lack of function pointers. But now! Now I have a hard time writing code in Java, because I keep wanting to solve my problems using blocks.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Rails&lt;/b&gt; - while Rails is just a framework, and not truly a language, I feel it deserves its own entry. Rails leverages Ruby in such a way that it feels like you are using a &lt;a href="http://en.wikipedia.org/wiki/Domain-specific_language"&gt;domain specific language&lt;/a&gt;. Cool ideas I learned from Rails are &lt;a href="http://en.wikipedia.org/wiki/Convention_over_configuration"&gt;convention over configuration&lt;/a&gt; and how to actually use the &lt;a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;MVC pattern&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Javascript&lt;/b&gt; - from Javascript I learned that OO can be done in multiples ways. OO in Javascript is nothing like OO in C++/Java/C#. In fact, everything is just an &lt;a href="http://en.wikipedia.org/wiki/Associative_array"&gt;associative array&lt;/a&gt;. The power of what you can do with such a simple idea, in conjunction with closures, is eye opening.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;I am not a language expert, and as such there are many important features of the above languages that I omitted. The point of this post isn't what features are in what language. Rather it is a personal history showing how new languages expanded my horizons and abilities as a software developer.&lt;br /&gt;&lt;br /&gt;Have you learned a new language lately?&lt;br /&gt;&lt;div class="medium"&gt;&lt;br /&gt;&lt;dl&gt;&lt;/dl&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-5015707707474917961?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/5015707707474917961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=5015707707474917961' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5015707707474917961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5015707707474917961'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/04/new-languages-can-broaden-your-horizons.html' title='New Languages Can Broaden Your Horizons'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-2392960589046730180</id><published>2011-04-04T09:00:00.004-04:00</published><updated>2011-04-04T11:47:02.440-04:00</updated><title type='text'>Role Based Authorization in Rails</title><content type='html'>Previously, I created an &lt;a href="http://madkingsmusings.blogspot.com/2011/03/google-openid-via-rails.html"&gt;OpenID based login system&lt;/a&gt;. Now I want to create a role based authorization system. What do I mean by role based authorization? Well, in many systems you have actions that you want to protect so that only admins can perform them. Obviously, we could implement this by adding an "admin" flag to our User model. However, sometimes, we have more than just two classes of users. For example, if you have a website with comments, you might want to have a class of users who aren't full admins, but do have authority to filter comments for spam.&lt;br /&gt;&lt;br /&gt;A simple way to accomplish all of this is to have Role model and then let each user have 0 or more roles associated with them. Here is how you can do this, assuming that you've already got an authentication system as described in the &lt;a href="http://madkingsmusings.blogspot.com/2011/03/google-openid-via-rails.html"&gt;previous post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Data Model&lt;/span&gt;&lt;br /&gt;First, create database tables, which will just consist of a Role table with a name column, and then a mapping table to map roles to users.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate model Role name:string&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate model RolesUser role_id:integer user_id:integer&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rake db:migrate&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then tell the User model about the roles, by adding the following line to the User class (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/models/user.rb&lt;/span&gt;):&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;has_and_belongs_to_many :roles, uniq&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Role Views&lt;/span&gt;&lt;br /&gt;Now that we have a Role model, we need a way to add and delete roles. Since this is functionality that we'll want to reserve only to top level admins, let's put this functionality in an admin directory.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate controller Admin::Roles index&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This generated the controller and view, but not the right routes. So delete the&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;get "roles/index"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;line that was added to routes.rb file and replace it with:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;namespace :admin &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;  resources :roles, :only&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:index, :create, :destroy&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;This allows a showing of roles and the RESTful creation and destruction of roles. Since roles are just a name, we don't really need the separate &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;show&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;edit&lt;/span&gt;, and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update&lt;/span&gt; paths.&lt;br /&gt;&lt;br /&gt;The controller (at &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/controllers/admin/roles_controller.rb&lt;/span&gt;) looks like:&lt;br /&gt;&lt;pre style="background-attachment: initial; background-clip: initial; background-color: black; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; Admin::RolesController &amp;lt; ApplicationController&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; index&lt;br /&gt;    @roles &lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; Role&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;all&lt;br /&gt;    @new_role &lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; Role&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;new&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; create&lt;br /&gt;    Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;create params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;span style="color: #d2cd86;"&gt;&lt;/span&gt;    redisplay_roles&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; destroy&lt;br /&gt;    Role&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;find&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;params&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;:id&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;destroy&lt;br /&gt;    redisplay_roles&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;br /&gt;&lt;br /&gt;  private&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt; redisplay_roles&lt;br /&gt;    redirect_to admin_roles_path&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #d1d1d1;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;The method &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;redisplay_roles&lt;/span&gt; is a placeholder for now. A little bit further down we'll modify this to support adding of roles via Ajax calls.&lt;br /&gt;&lt;br /&gt;So now we have to create the HTML, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/views/admin/roles/index.html.haml&lt;/span&gt;&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;%h1 Roles&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; render &lt;span style="color: #00c4c4;"&gt;'role_list'&lt;/span&gt;&lt;br /&gt;%br&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; form_for &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:admin, @new_role&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |f|&lt;br /&gt;  &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;label :name, &lt;span style="color: #00c4c4;"&gt;'Name of new role:'&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;text_field :name&lt;br /&gt;  &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; f&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;submit&lt;/pre&gt;&lt;div&gt;There are two things of note here. On line 4, the array &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;[:admin, @new_role]&lt;/span&gt; results in the URL for creating a Role object in the admin namespace, which is what we want. On line 2, we render the partial 'role_list' (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/views/admin/roles/_role_list.html.haml&lt;/span&gt;), which is shown below. The reason it is a partial is to make it easier to Ajaxify everything.&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #9999a9;"&gt;#RoleList&lt;/span&gt;&lt;br /&gt;  %h2 There &lt;span style="color: #9999a9;"&gt;#{is_pluralize(@roles.length, 'role')}&lt;/span&gt;&lt;br /&gt;  %ul&lt;br /&gt;    &lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; @roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |role|&lt;br /&gt;      %li&lt;br /&gt;        %b&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;name&lt;br /&gt;        &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #9999a9;"&gt;#{link_to 'x', [:admin, role], :method=&amp;gt;:delete, :confirm=&amp;gt;'You sure?'}]&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;is_pluralize&lt;/span&gt; on line 2 is a helper function I wrote (in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;application_helper.rb&lt;/span&gt;) that uses the appropriate form of to be along with the pluralized noun. It looks like:&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; is_pluralize&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;count, noun&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;  verb &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;count &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt; ? &lt;span style="color: #00c4c4;"&gt;"is"&lt;/span&gt; : &lt;span style="color: #00c4c4;"&gt;"are"&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #00c4c4;"&gt;"#{verb} #{pluralize(count, noun)}"&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;The rest of the partial is just looping through the roles and showing them. On line 7, a link to the destroy action is created, by specifying the method as '&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;delete&lt;/span&gt;'.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;AJAX&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The above is all you need to be able to add and delete roles. Since we are redirecting back to the index page after each add or delete, I thought it would be nice to make those Ajax actions, and just stay on the page. Turns out this is pretty easy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As a first step, if you haven't already, install &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;. (yes you could use &lt;a href="http://www.prototypejs.org/"&gt;prototype&lt;/a&gt; or many other javascript libraries, but jQuery is what I am using, so it's what I'll describe.) To do this, put the line&lt;/div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;gem 'jquery-rails'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;in your Gemfile and then run the commands:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;bundle install&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails g jquery:install&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that jQuery is installed, modify the add/remove calls to be AJAX calls by adding the parameter "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;:remote=&amp;gt;true&lt;/span&gt;" to the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;link_to&lt;/span&gt;&amp;nbsp;call on line 7 of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_role_list.html.haml&lt;/span&gt; and to the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;form_for&lt;/span&gt;&amp;nbsp;call on line 4 of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;index.html.haml&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Now we modify&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;redisplay_roles&lt;/span&gt;&amp;nbsp;to look like&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; redisplay_roles&lt;br /&gt;  respond_to &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;|&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;html &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; redirect_to admin_roles_path &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;js &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;&lt;br /&gt;      @roles &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;all&lt;br /&gt;      render :redisplay_roles&lt;br /&gt;    &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;Line 3 handles the case if the method is called in a non-Ajax way. Line 5 ensures that &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@roles&lt;/span&gt; variable is set. Line 6 is where the magic happens. Rather than returning html like normal, we will return javascript, so we create a view file &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/views/admin/roles/redisplay_roles.js.haml&lt;/span&gt;. This javascript HAML file just has one line:&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;$&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;'#RoleList'&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;replaceWith&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #00c4c4;"&gt;"#{escape_javascript(render :partial=&amp;gt;'role_list')}"&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;$('#RoleList')&lt;/span&gt; is the jQuery selector to choose the div we are modifying and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;replaceWith&lt;/span&gt; is a jQuery function which will replace the contents of that div with the argument that is passed in. To create that argument, we just render the partial '&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;role_list&lt;/span&gt;' again.&lt;br /&gt;&lt;br /&gt;With just those simple changes, now the adding and removing of roles is done via Ajax calls.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;User Views&lt;/span&gt;&lt;br /&gt;Now that we can create roles, we need to be able to add roles to users. This will be very similar to what we did with Roles above. The command:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate controller Admin::Users index show&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;generates our controllers and views. Again, we will delete what got added to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;routes.rb&lt;/span&gt; and replace it with:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;namespace :admin &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;  resources :users &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;    member &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;      post :add_role&lt;br /&gt;      delete :delete_role&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  resources :roles, :only&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:index, :create, :destroy&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;This includes the route for Roles from above. &amp;nbsp;While we aren't using the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;edit&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update&lt;/span&gt; routes, we will leave them in for future enhancements. We are also adding the routes &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;add_role&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;delete_role&lt;/span&gt; which will do what you'd expect. The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;users_controller&lt;/span&gt; looks like:&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;class&lt;/span&gt; Admin::UsersController &amp;lt; ApplicationController&lt;br /&gt;  before_filter :load_user, :except&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:index&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; index&lt;br /&gt;    @users &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; User&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;all&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; show&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; add_role&lt;br /&gt;    role &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;find_by_name params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;    @user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;push role &lt;span style="color: #e66170; font-weight: bold;"&gt;if&lt;/span&gt; role&lt;br /&gt;    redisplay_roles&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; delete_role&lt;br /&gt;    @user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;delete&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;find params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;    redisplay_roles&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  private&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; load_user&lt;br /&gt;    @user &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; User&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;find params&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:id&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; redisplay_roles&lt;br /&gt;    respond_to &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |&lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;|&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;html &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; redirect_to &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:admin, @user&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #e66170; font-weight: bold;"&gt;format&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;js &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; render :redisplay_roles &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;index.html.haml&lt;/span&gt; just shows a list of all the users with links to show them.&amp;nbsp;&lt;/div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;%h1 Users&lt;br /&gt;%h2 There &lt;span style="color: #9999a9;"&gt;#{is_pluralize(@users.length, 'user')}&lt;/span&gt;&lt;br /&gt;%ul&lt;br /&gt;  &lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; @users&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |user|&lt;br /&gt;    %li&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; link_to user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;email, &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:admin, user&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;show.html.haml&lt;/span&gt; looks like:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;%h1 User&lt;br /&gt;%b Id:&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;identifier_url&lt;br /&gt;%br&lt;br /&gt;%b Name:&lt;br /&gt;&lt;span style="color: #9999a9;"&gt;#{@user.last_name}, #{@user.first_name}&lt;/span&gt;&lt;br /&gt;%br&lt;br /&gt;%b Email:&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; @user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;email&lt;br /&gt;%hr&lt;br /&gt;%h2 Roles&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; render :partial&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #00c4c4;"&gt;'role_list'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #9999a9;"&gt;#addRole&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; form_tag &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:add_role, :admin, @user&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;, :remote&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;true&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; label_tag :role, &lt;span style="color: #00c4c4;"&gt;'Add Role:'&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; text_field_tag :role&lt;br /&gt;    &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; submit_tag &lt;span style="color: #00c4c4;"&gt;'Add'&lt;/span&gt;&lt;br /&gt;%hr&lt;br /&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; link_to &lt;span style="color: #00c4c4;"&gt;'Return to User List'&lt;/span&gt;, &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:admin, User&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Just like above, we put the roles listing in a partial. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_role_list.html.haml&lt;/span&gt; looks very similar to the one in the roles view:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #9999a9;"&gt;#RoleList&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;This user has &lt;span style="color: #9999a9;"&gt;#{pluralize @user.roles.length, 'role'}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;%ul&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #d2cd86;"&gt;-&lt;/span&gt; @user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;each&lt;/span&gt; &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |role|&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;%li&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;name&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;&lt;span style="color: #9999a9;"&gt;#{link_to 'x', delete_role_admin_user_path(@user, :role=&amp;gt;role.id), :method=&amp;gt;:delete, :confirm=&amp;gt;'Are you sure?', :remote=&amp;gt;true}]&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;which means that &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;redisplay_roles.js.haml&lt;/span&gt; is identical to the Roles version of this. I suppose I could pull this into the shared directory and let it be shared, but I am not convinced that it will always remain identical, so I'll leave the duplication for now.&lt;/div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Authorization&lt;/span&gt;&lt;br /&gt;Now that we can create roles, and add users to roles we need a way to use this information. First we'll add a method to User to determine if a user has any of a given role or roles. I would like this to work if the passed in roles are Role objects or strings or symbols defining the role name. To do this I overrode &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;to_s&lt;/span&gt; in Role&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; to_s&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;name&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;and added the following methods to User&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; has_role?&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;role_names&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;self&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;index &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;|role| includes_role role_names, role&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; includes_role&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;role_list, role&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;role_list&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;index &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt;|r| r&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;to_s &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;name&lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;As you can see, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;has_role?&lt;/span&gt; takes a variable number of roles and returns true if the user has any of those roles. Now that we can make this determination, we will create a method called &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ensure_role&lt;/span&gt;. We could place it in the controller, but since we will be using it from multiple controllers we will be placing it in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;authentication_helper.rb&lt;/span&gt; that was created in the &lt;a href="http://madkingsmusings.blogspot.com/2011/03/google-openid-via-rails.html"&gt;previous post&lt;/a&gt;. It looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;def&lt;/span&gt; ensure_role&lt;span style="color: #d2cd86;"&gt;(&lt;/span&gt;&lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;role_names&lt;span style="color: #d2cd86;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&lt;/span&gt; signed_in?&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;role_names&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;push &lt;span style="color: #00c4c4;"&gt;'admin'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;unless&lt;/span&gt; current_user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;has_role? &lt;span style="color: #d2cd86;"&gt;*&lt;/span&gt;role_names&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;flash&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:error&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;'You do not have permission to view this page'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;redirect_to actions_index_path&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;else&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;ensure_signed_in&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;On line 3, we push the role 'admin' onto the list of roles. We do this, because we want the 'admin' role to have all permissions and this way we don't have to explicitly list it every time we are ensuring a role. We add the line&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;before_filter &lt;span style="color: #b060b0;"&gt;{&lt;/span&gt; ensure_role &lt;span style="color: #00c4c4;"&gt;'admin'&lt;/span&gt; &lt;span style="color: #b060b0;"&gt;}&lt;/span&gt;&lt;/pre&gt;to our controllers in the admin directory to ensure that only users with the admin role can access these pages. Technically we could leave off the 'admin' argument, since it is implicitly added, but I think it makes the controller much more readable to have it explicitly there in the case where 'admin' is the only role allowed.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Rake Task&lt;/span&gt;&lt;br /&gt;Here's a brief quiz. Do you see the problem that we've created?&lt;br /&gt;&lt;br /&gt;We now have a chicken and egg problem. You can't create an admin role or assign it to yourself unless you already have it. The way we'll solve this is by creating rake tasks that you can run at the command line to create roles and assign them to users. All the details of rake are beyond the scope of this blog post which has gone on for way to long already, so I'll just tell you what I did. I created a file &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;lib/tasks/roles.rake&lt;/span&gt; which looks like:&lt;br /&gt;&lt;pre style="background: #000000; color: #d1d1d1;"&gt;namespace :roles &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;desc &lt;span style="color: #00c4c4;"&gt;'Creates a role'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;task :create, &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role_name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; :environment &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |cmd, args|&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;create :name&lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt;args&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role_name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;puts&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"Created role #{args[:role_name]}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;desc &lt;span style="color: #00c4c4;"&gt;'Add User to Role'&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;task :add_user, &lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:email, :role_name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt; &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt;&amp;gt; :environment &lt;span style="color: #e66170; font-weight: bold;"&gt;do&lt;/span&gt; |cmd, args|&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;user &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; User&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;find_by_email args&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:email&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;role &lt;span style="color: #d2cd86;"&gt;=&lt;/span&gt; Role&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;find_by_name args&lt;span style="color: #d2cd86;"&gt;[&lt;/span&gt;:role_name&lt;span style="color: #d2cd86;"&gt;]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;unless&lt;/span&gt; user&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;puts&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"No such user #{args[:email]}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;unless&lt;/span&gt; role&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;puts&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"No such role #{args[:role_name]}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;return&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;user&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;roles&lt;span style="color: #d2cd86;"&gt;.&lt;/span&gt;push role&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;puts&lt;/span&gt; &lt;span style="color: #00c4c4;"&gt;"added #{role.name} to #{user.last_name}, #{user.first_name}"&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #e66170; font-weight: bold;"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;div&gt;Now you can run the commands:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rake roles:create[admin]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rake roles:create[&amp;lt;your email address&amp;gt;,admin]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;to create an admin role and assign your user to it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Conclusion&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Wow, this post went on a lot longer than I meant it to. I guess there was a lot more ground to cover than I realized. I'll try to keep my tutorial based posts shorter in the future...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-2392960589046730180?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/2392960589046730180/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=2392960589046730180' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2392960589046730180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/2392960589046730180'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/04/role-based-authorization-in-rails.html' title='Role Based Authorization in Rails'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6699251859304586372</id><published>2011-03-28T09:00:00.001-04:00</published><updated>2011-03-28T09:00:13.137-04:00</updated><title type='text'>Visiting a Foreign Land</title><content type='html'>This past week I took a trip to a foreign country, MacLand. I have many friends who either live there or are frequent visitors and they all love it, despite the very high cost of living there. I decided it was time to see what all the fuss was about.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://lh5.googleusercontent.com/-9a3hCdHrtyM/TYZcQodMTZI/AAAAAAAAAsU/19JymMUYe3k/s1600/ScreenCap2-crop.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="237" src="https://lh5.googleusercontent.com/-9a3hCdHrtyM/TYZcQodMTZI/AAAAAAAAAsU/19JymMUYe3k/s400/ScreenCap2-crop.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;When I first got off the plane and opened up my new &lt;a href="http://www.apple.com/macbookpro/"&gt;13" MacBook Pro&lt;/a&gt;, it was welcoming. I was through security and on the internet in almost no time at all. However, it didn't take long for me to become discombobulated. The customs here are strange,&amp;nbsp;like the fact that they drive on the left side of the road here with their window controls and they don't leave the menus at the tables as I am used to. I'm obviously going to have to learn the local dialect.&amp;nbsp;What is a "&lt;a href="http://www.apple.com/macosx/what-is-macosx/dock-and-finder.html"&gt;Finder&lt;/a&gt;" or "&lt;a href="http://en.wikipedia.org/wiki/Preview_(software)"&gt;Preview&lt;/a&gt;" anyway, and what are the funny symbols (arrows and cloverleafs?) I am seeing on many of the menus?&amp;nbsp;And my instincts as to what to do in any given situation tend to be wrong. I am worried that I'll commit a serious faux pas and end up in trouble. Well, I guess I'll just have to trust that my friends that live here will bail me out if that happens.&lt;br /&gt;&lt;br /&gt;To achieve some level of comfort, I&amp;nbsp;installed &lt;a href="http://www.google.com/chrome/intl/en/landing_chrome_mac.html?hl=en"&gt;Chrome&lt;/a&gt;&amp;nbsp;and &lt;a href="http://emacsformacosx.com/"&gt;Emacs&lt;/a&gt;. It's always nice to see a familiar face when abroad.&amp;nbsp;In the lobby of my MacBook there are a number of brochures of local activities, like going on &lt;a href="http://www.apple.com/safari/"&gt;Safari&lt;/a&gt;, that I suppose I should try out. When I get up some courage, I guess I will try to branch away from the strictly familiar.&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;So why did I decide to take this journey? Well some years ago, there was a revolution in MacLand, and the new government, OS X, was based on Unix. I have long preferred Unix to other approaches, but historically, Unix run places were not places for individuals to dwell, unless they had a strong Do It Yourself mentality. My own hobby interests lie elsewhere, I just want my infrastructures to work. My couple personal trips to Unix resulted in more hassles than it was worth to me. With OS X, MacLand promised the power of Unix while also providing a nice friendly infrastructure that just works.&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Between this promise, the numerous friends I have who love it, and the fact that Ruby on Rails is supposed to be &lt;a href="http://oomoo.wordpress.com/2008/03/31/is-mac-perfect-for-rails-development/"&gt;easier in MacLand&lt;/a&gt;, I decided it was time to take the trip. We'll see how I like it, but so far I am cautiously optimistic that this will be a good land. &amp;nbsp;(as long as there are no betrayals)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://3.gvt0.com/vi/znxFrgql5dc/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/znxFrgql5dc&amp;fs=1&amp;source=uds" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266" src="http://www.youtube.com/v/znxFrgql5dc&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6699251859304586372?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6699251859304586372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6699251859304586372' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6699251859304586372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6699251859304586372'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/03/visiting-foreign-land.html' title='Visiting a Foreign Land'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-9a3hCdHrtyM/TYZcQodMTZI/AAAAAAAAAsU/19JymMUYe3k/s72-c/ScreenCap2-crop.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6004341652359564387</id><published>2011-03-21T09:00:00.007-04:00</published><updated>2011-03-21T09:00:20.869-04:00</updated><title type='text'>Google OpenID via Rails</title><content type='html'>So you've decided to write a web application. You are probably going to want to allow users to log in. Maybe its because you don't want to &lt;a href="http://www.codinghorror.com/blog/2007/09/youre-probably-storing-passwords-incorrectly.html"&gt;mess up password security&lt;/a&gt;, or maybe its because you feel &lt;a href="http://www.codinghorror.com/blog/2008/05/openid-does-the-world-really-need-yet-another-username-and-password.html"&gt;the web doesn't need yet one more username/password combination&lt;/a&gt;, but you've decided to use &lt;a href="http://openid.net/"&gt;OpenID&lt;/a&gt;. Here's how I've done it in rails.&lt;br /&gt;&lt;br /&gt;For this, I am very much leveraging&amp;nbsp;&lt;a href="http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html"&gt;http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html&lt;/a&gt;. Rather than repeat everything that Seth Ladd said, I will just include the differences from him.&lt;br /&gt;&lt;br /&gt;First, I am starting completely from scratch, so the first step is to create a rails project.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails new login&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then I need a user model:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate model User identifier_url:string email:string&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;first_name:string last_name:string&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rake db:migrate&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The above two steps replace step 0 from &lt;a href="http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html"&gt;Seth Ladd's blog post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For step 1, here is what my Gemfile looks like:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; source &lt;span style="color: maroon;"&gt;'http://rubygems.org'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'rails'&lt;/span&gt;, &lt;span style="color: maroon;"&gt;'3.0.4'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'sqlite3'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'haml'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'haml-rails'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'ruby-openid'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'rack-openid'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt; gem &lt;span style="color: maroon;"&gt;'mongrel'&lt;/span&gt;, &lt;span style="color: maroon;"&gt;'&amp;gt;= 1.2.0.pre2'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;group&lt;/span&gt; :development &lt;span style="color: blue;"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;   gem &lt;span style="color: maroon;"&gt;'rspec-rails'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt; end &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt; &lt;span style="color: #2b91af;"&gt;group&lt;/span&gt; :test &lt;span style="color: blue;"&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt;   gem &lt;span style="color: maroon;"&gt;'rspec'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 17&lt;/span&gt;   gem &lt;span style="color: maroon;"&gt;'webrat'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 18&lt;/span&gt; end&lt;/pre&gt;There are only a couple things of note in this Gemfile. As &lt;a href="http://madkingsmusings.blogspot.com/2011/01/learning-haml.html"&gt;I've mentioned before&lt;/a&gt;, I like haml, hence lines 5-6. Lines 7-8 are the lines from &lt;a href="http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html"&gt;Seth's post&lt;/a&gt;. Line 10 is because &lt;a href="http://stackoverflow.com/questions/4926740/omniauth-google-openid-webrickhttpstatusrequesturitoolarge"&gt;WEBrick causes errors&lt;/a&gt;&amp;nbsp;when I try to use the OpenID authentication and the current version of mongrel isn't working for me (Windows platform). Oh, and lines 11-18 are lines I pulled from &lt;a href="http://www.amazon.com/Ruby-Rails-Tutorial-Addison-Wesley-Professional/dp/0321743121/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1300156412&amp;amp;sr=8-1"&gt;Ruby On Rails 3 Tutorial&lt;/a&gt; by Michael Hartl. Even though I am not doing any testing here, it seems like a good idea to enable it.&lt;br /&gt;&lt;br /&gt;Then, of course, I ran:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;bundle install&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Steps 2-6 I exactly followed &lt;a href="http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html"&gt;Seth's post&lt;/a&gt;. For step 7, I basically followed his post, but used Haml rather than Erb. For step 8, I created an action controller:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails generate controller Actions view&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;My action controller looks like:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; ActionsController &amp;lt; ApplicationController&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;   before_filter &lt;span style="color: blue;"&gt;:&lt;/span&gt;ensure_signed_in  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;   def view &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;   &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; &lt;span style="color: blue;"&gt;end&lt;/span&gt;&lt;/pre&gt;and my app/views/actions/view.html.haml looks like:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; %p  &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;   = current_user.first_name  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   = current_user.last_name  &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;   (  &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;   = current_user.email  &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;   )  &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt; %p= current_user.identifier_url&lt;/pre&gt;And with that I have a simple app that uses OpenID for authentication. I test it by running:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;rails s&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and then going to&amp;nbsp;&lt;a href="http://127.0.0.1:3000/actions/view"&gt;http://127.0.0.1:3000/actions/view&lt;/a&gt;&amp;nbsp;in my browser. As expected, I get redirected to google, and then after logging in, I get sent back, and I can see my information. Doing something useful with this will be left as an exercise for the reader.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6004341652359564387?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6004341652359564387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6004341652359564387' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6004341652359564387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6004341652359564387'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/03/google-openid-via-rails.html' title='Google OpenID via Rails'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-5262438092922345497</id><published>2011-03-14T09:00:00.001-04:00</published><updated>2011-03-14T09:00:17.424-04:00</updated><title type='text'>When Does X Not Equal X and Other Oddities</title><content type='html'>In many programming languages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;For what value of x is the condition (x == x) false?&lt;/li&gt;&lt;li&gt;For what integer value of y is the condition (y &amp;lt; 0) &amp;amp;&amp;amp; (-y &amp;lt; 0) true?&lt;/li&gt;&lt;/ul&gt;i.e. define x on line 3 and y on line 10 &amp;nbsp;in the following Java code sample so that lines 7 and 12 execute:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Odd {&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;   &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;final&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; main(String[] arg) {&lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;     &lt;span style="color: green;"&gt;// define x here&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;     &lt;span style="color: blue;"&gt;if&lt;/span&gt; (x == x) { &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;       System.out.printf(&lt;span style="color: maroon;"&gt;"Reflexive%n"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;     } &lt;span style="color: blue;"&gt;else&lt;/span&gt; { &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;       System.out.printf(&lt;span style="color: maroon;"&gt;"Not Reflexive%n"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;     } &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     &lt;span style="color: blue;"&gt;int&lt;/span&gt; y = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;; &lt;span style="color: green;"&gt;// change 0 to something else&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;     &lt;span style="color: blue;"&gt;if&lt;/span&gt; (y &amp;lt; &lt;span style="color: maroon;"&gt;0&lt;/span&gt; &amp;amp;&amp;amp; -y &amp;lt; &lt;span style="color: maroon;"&gt;0&lt;/span&gt;) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;       System.out.printf(&lt;span style="color: maroon;"&gt;"How can they both be negative?%n"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;     } &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt; }&lt;/pre&gt;In SQL:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What database value x is the condition &lt;code&gt;(x = x)&lt;/code&gt; false?&lt;/li&gt;&lt;li&gt;What about values y and z such that &lt;code&gt;((y = z) OR (y != z))&lt;/code&gt; is false?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;These next few lines are intentionally left blank, in case you wan to think about these before viewing possible answers.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Answers&lt;/span&gt;&lt;br /&gt;In mathematics, one of the properties of the equality operator is that it is &lt;a href="http://www.mathwords.com/r/reflexive_property.htm"&gt;reflexive&lt;/a&gt;. It turns out that the Java (and almost every other language) equality operator does not have this property. &amp;nbsp;If you put the following on line 3:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;double&lt;/span&gt; x = &lt;span style="color: maroon;"&gt;0&lt;/span&gt;&lt;span style="color: maroon;"&gt;.0&lt;/span&gt;/&lt;span style="color: maroon;"&gt;0&lt;/span&gt;&lt;span style="color: maroon;"&gt;.0&lt;/span&gt;;&lt;/pre&gt;and run the program, it'll print out "Not Reflexive". &amp;nbsp;Why is this? &amp;nbsp;Well, according to the &lt;a href="http://steve.hollasch.net/cgindex/coding/ieeefloat.html"&gt;IEEE standard 754&lt;/a&gt;, 0/0 = NaN. What is NaN? &amp;nbsp;Well, it is not a number, corresponding to the mathematical concept of an &lt;a href="http://mathforum.org/dr.math/faq/faq.divideby0.html"&gt;indeterminate number&lt;/a&gt;. Because it could be any number, the powers that be decided that NaN != NaN.&lt;br /&gt;&lt;br /&gt;So what about the second example? Well, in a 2's complement system of encoding numbers, there is room for one more negative number than positive numbers. (e.g. a 16 bit signed value encodes the values -32,768 to 32,767). What this means is that if you try to negate the most negative number (e.g. -32,768), the corresponding positive number won't fit in the memory location, and so you actually get the same number back. This means an answer to line 10 is:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt; y = Integer.MIN_VALUE;&lt;/pre&gt;&lt;br /&gt;What about the SQL questions? Well, it turns out that &lt;a href="http://en.wikipedia.org/wiki/Null_%28SQL%29"&gt;SQL treats NULL like NaN&lt;/a&gt;, i.e. it is an indeterminate value. SQL goes even further than Java. &amp;nbsp;Not only is the expression (NULL = NULL) result in false, but (NULL != NULL) also results in false. Well, actually that's not quite true. SQL uses &lt;a href="http://en.wikipedia.org/wiki/Null_%28SQL%29"&gt;three-valued logic&lt;/a&gt;, and so both the expressions actually evaluate to undefined, but undefined is treated as false when it comes to matching rows.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Why does this matter?&lt;/span&gt;&lt;br /&gt;Have you ever written code where it is assumed that if two numbers have the same value they will be equal? How about that &lt;code&gt;Math.abs()&lt;/code&gt; will always return a negative value?&amp;nbsp;&lt;a href="http://blogs.msdn.com/b/ericlippert/about.aspx"&gt;Eric Lipper&lt;/a&gt;t had a blog post showing how this assumption can cause the &lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2011/01/24/spot-the-defect-bad-comparisons-part-two.aspx"&gt;standard sort functions to fail&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I personally have generated two reports by running queries like:&lt;br /&gt;&lt;code&gt;SELECT * FROM Table WHERE columnA = columnB -- report 1&lt;/code&gt;&lt;br /&gt;&lt;code&gt;SELECT * FROM Table WHERE columnA != columnB -- report 2&lt;/code&gt;&lt;br /&gt;and then was mightily confused when, at some point during the analysis, I realized that there were rows in my database table that weren't in either report.&lt;br /&gt;&lt;br /&gt;These may feel like nitpicky details that only show up on those gotcha type quizzes. However, not understanding these details can lead to bugs in your code. If you are going to be an expert programmer, you have to know and understand the rough edges of your language.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-5262438092922345497?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/5262438092922345497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=5262438092922345497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5262438092922345497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/5262438092922345497'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/03/when-does-x-not-equal-x-and-other.html' title='When Does X Not Equal X and Other Oddities'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-7951457996832472009</id><published>2011-03-07T09:00:00.043-05:00</published><updated>2011-03-07T09:00:00.417-05:00</updated><title type='text'>Crop and Upload Image - Server Side</title><content type='html'>&lt;a href="http://madkingsmusings.blogspot.com/2011/02/crop-and-upload-image-client-side.html"&gt;Previously I showed&lt;/a&gt; some HTML and JavaScript for cropping and uploading an image. Here is what I did on the server side to support this.&lt;br /&gt;&lt;br /&gt;First I make sure I had &lt;a href="http://wiki.rubyonrails.org/getting-started/installation"&gt;rails 3 installed&lt;/a&gt;, &lt;a href="http://stackoverflow.com/questions/99211/how-do-i-get-haml-to-work-with-rails"&gt;configured for HAML&lt;/a&gt;&amp;nbsp;and &lt;a href="http://www.blog.bridgeutopiaweb.com/post/how-to-use-jquery-for-rails-3/"&gt;jquery&lt;/a&gt;. I also download the &lt;a href="http://jqueryui.com/"&gt;jqueryui&lt;/a&gt;&amp;nbsp;and &lt;a href="http://deepliquid.com/content/Jcrop.html"&gt;jcrop&lt;/a&gt;&amp;nbsp;javascript and css files and add them to my project. &amp;nbsp;Then, I use the rails scaffolding functionality to get started:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ruby script\rails generate scaffold image content:binary width:integer height:integer name:string&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;After running:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;rake db:migrate&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I have a database table for storing an uploaded image, including the binary content, the height and width of the image and a name. Now that I have a place to store the image, I need to modify the generated web pages, so I can upload it.&lt;br /&gt;&lt;br /&gt;Since I &lt;a href="http://madkingsmusings.blogspot.com/2011/01/learning-haml.html"&gt;prefer HAML&lt;/a&gt;, the first thing that I do is rename the two erb files that I need to change:&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/layouts/applications.html.erb&lt;/span&gt; to&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;views/layouts/applications.html.haml&lt;/span&gt; and&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/views/images/_form.html.erb&lt;/span&gt; to&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/views/images/_form.html.haml&lt;/span&gt;. &amp;nbsp;The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;applications.html&lt;/span&gt; file is the template for all the pages, and I need to modify that to allow pages to add their own javascript into the header. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_form.html&lt;/span&gt; is the partial that is used for both uploading new images and editing images.&lt;br /&gt;&lt;br /&gt;Converting the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;applications.html&lt;/span&gt; file from erb to haml is just a straightforward transformation. Here is what it looks like when I am done:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; !!! &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; %html &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   %head &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;     %title Image Upload &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;     = stylesheet_link_tag :all &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;     = javascript_include_tag :defaults &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;     = csrf_meta_tag &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;     = yield :head &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;   %body &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     = yield&lt;/pre&gt;The only real line of note is the named &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;yield&lt;/span&gt; on line 8. This is what will allow me to insert custom stylings and javascript into specific pages.&lt;br /&gt;&lt;br /&gt;The only thing left is to rewrite &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_form.html.haml&lt;/span&gt; so that it generates HTML and Javascript like discussed in the &lt;a href="http://madkingsmusings.blogspot.com/2011/02/crop-and-upload-image-client-side.html"&gt;client side post&lt;/a&gt;. Here is what this looks like.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; - content_for :head &lt;span style="color: blue;"&gt;do&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;   = stylesheet_link_tag &lt;span style="color: maroon;"&gt;'jquery.Jcrop'&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   = javascript_include_tag  &lt;span style="color: maroon;"&gt;'jquery.Jcrop.min'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;   %style &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;     -# insert styling described in client post here &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;   :javascript &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;     &lt;span style="color: green;"&gt;// insert javascript described in client post here&lt;/span&gt;&lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt; = form_for(@image) &lt;span style="color: blue;"&gt;do&lt;/span&gt; |f| &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;   - &lt;span style="color: blue;"&gt;if&lt;/span&gt; @image.errors.any? &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     #error_explanation &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;       %h2 &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;         = pluralize(@image.errors.count, &lt;span style="color: maroon;"&gt;"error"&lt;/span&gt;) &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;         prohibited this image from being saved:&lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;       %ul &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;         - @image.errors.full_messages.each &lt;span style="color: blue;"&gt;do&lt;/span&gt; |msg|&lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt;           %li= msg &lt;br /&gt;&lt;span style="color: teal;"&gt; 17&lt;/span&gt;   .field &lt;br /&gt;&lt;span style="color: teal;"&gt; 18&lt;/span&gt;     = f.label :name &lt;br /&gt;&lt;span style="color: teal;"&gt; 19&lt;/span&gt;     %br &lt;br /&gt;&lt;span style="color: teal;"&gt; 20&lt;/span&gt;     = f.text_field :name &lt;br /&gt;&lt;span style="color: teal;"&gt; 21&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 22&lt;/span&gt;   Drag and drop image here: &lt;br /&gt;&lt;span style="color: teal;"&gt; 23&lt;/span&gt;   #cropWidget{:width=&amp;gt;&lt;span style="color: maroon;"&gt;400&lt;/span&gt;, :height=&amp;gt;&lt;span style="color: maroon;"&gt;400&lt;/span&gt;} &lt;br /&gt;&lt;span style="color: teal;"&gt; 24&lt;/span&gt;     #surface &lt;br /&gt;&lt;span style="color: teal;"&gt; 25&lt;/span&gt;     %canvas#canv1{:width=&amp;gt;&lt;span style="color: maroon;"&gt;400&lt;/span&gt;, :height=&amp;gt;&lt;span style="color: maroon;"&gt;400&lt;/span&gt;} &lt;br /&gt;&lt;span style="color: teal;"&gt; 26&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 27&lt;/span&gt;   = f.hidden_field :content64 &lt;br /&gt;&lt;span style="color: teal;"&gt; 28&lt;/span&gt;   Uploaded image is here: &lt;br /&gt;&lt;span style="color: teal;"&gt; 29&lt;/span&gt;   %canvas#canv2{:width=&amp;gt;&lt;span style="color: maroon;"&gt;200&lt;/span&gt;, :height=&amp;gt;&lt;span style="color: maroon;"&gt;200&lt;/span&gt;} &lt;br /&gt;&lt;span style="color: teal;"&gt; 30&lt;/span&gt;   .field &lt;br /&gt;&lt;span style="color: teal;"&gt; 31&lt;/span&gt;     = f.label :width &lt;br /&gt;&lt;span style="color: teal;"&gt; 32&lt;/span&gt;     %br &lt;br /&gt;&lt;span style="color: teal;"&gt; 33&lt;/span&gt;     = f.text_field :width, :readOnly=&amp;gt;&lt;span style="color: maroon;"&gt;true&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 34&lt;/span&gt;   .field &lt;br /&gt;&lt;span style="color: teal;"&gt; 35&lt;/span&gt;     = f.label :height &lt;br /&gt;&lt;span style="color: teal;"&gt; 36&lt;/span&gt;     %br &lt;br /&gt;&lt;span style="color: teal;"&gt; 37&lt;/span&gt;     = f.text_field :height, :readOnly=&amp;gt;&lt;span style="color: maroon;"&gt;true&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 38&lt;/span&gt;   .actions &lt;br /&gt;&lt;span style="color: teal;"&gt; 39&lt;/span&gt;     = f.submit :id=&amp;gt;&lt;span style="color: maroon;"&gt;"imageSubmit"&lt;/span&gt;, :onClick=&amp;gt;&lt;span style="color: maroon;"&gt;"uploadSelection();"&lt;/span&gt;, :disabled=&amp;gt;&lt;span style="color: maroon;"&gt;true&lt;/span&gt;;&lt;/pre&gt;I left out the specific CSS styles and JavaScript as they are described in the &lt;a href="http://madkingsmusings.blogspot.com/2011/02/crop-and-upload-image-client-side.html"&gt;previous post&lt;/a&gt;. Notice that lines 1-7 are inserted into the head of the template as described by &lt;code&gt;yield :head&lt;/code&gt; on line 8 of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;application.html.haml&lt;/span&gt; file above. Lines 9-16 are the boilerplate error handling that was generated by the scaffold command. The remainder just generates the HTML for displaying the image and for entering the name of the image.&lt;br /&gt;&lt;br /&gt;We are now almost, but not quite, done. If you notice the field that we are storing the image content in is content64, while the database column for the binary data is just called content. We need to take the base64 encoding of the image that is sent over the wire as part of the HTML POST and decode it to binary before saving in the database. To do that, we just need to add new methods in our &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;app/models/image.rb&lt;/span&gt; Image class.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;require&lt;/span&gt; &lt;span style="color: maroon;"&gt;"base64"&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Image &amp;lt; ActiveRecord::Base &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   def content64 &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;     &lt;span style="color: blue;"&gt;return&lt;/span&gt; nil unless content &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;     &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: maroon;"&gt;"data:image/png;base64,"&lt;/span&gt; + Base64.encode64(content); &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;   end &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;   def content64=(c64) &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;     index = c64.index(&lt;span style="color: maroon;"&gt;','&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     self.content = Base64.decode64(c64[index..-&lt;span style="color: maroon;"&gt;1&lt;/span&gt;]); &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;   end &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt; end&lt;/pre&gt;This way when the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;content64&lt;/span&gt; attribute is assigned in our controller class, it'll actually write the binary data to the content field so it can be saved to the database. The only clever part of this is that the content64 data that is passed from the HTML form has the initial string &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;"data:image/png;base64,"&lt;/span&gt;. &amp;nbsp;Lines 9-10 calculate where this prefix ends and just decodes the remainder of the string. Line 5 adds this prefix back on to the encoded binary content.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Other Work&lt;/span&gt;&lt;br /&gt;So the above will allow you to upload a new image that has been cropped and save it to the database. However, to be able to view that image or edit it, more work is needed. Probably the most important thing to do is to add a new action which will return the image's binary content. The url for this action can be used in the src attribute of &amp;lt;img&amp;gt; tags. The other scaffold generated pages for viewing and listing the uploaded images are then modified to display the image rather than showing the binary content. The last step is to modify the &lt;a href="http://madkingsmusings.blogspot.com/2011/02/crop-and-upload-image-client-side.html"&gt;javascript described previously&lt;/a&gt;&amp;nbsp;to check if an image already exists (i.e. we are editing an existing uploaded image rather than uploading a new image), and preload that. Since this post has gone on long enough, I will leave all of those details as an exercise for the reader.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-7951457996832472009?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/7951457996832472009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=7951457996832472009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7951457996832472009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/7951457996832472009'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/03/crop-and-upload-image-server-side.html' title='Crop and Upload Image - Server Side'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-4655232007479509740</id><published>2011-02-28T09:00:00.001-05:00</published><updated>2011-02-28T09:00:24.538-05:00</updated><title type='text'>The Problem with Component Based Web Architectures</title><content type='html'>Imagine the following scenario:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You browse to a web site and log in.&lt;/li&gt;&lt;li&gt;After doing whatever you needed to do, you log out.&lt;/li&gt;&lt;li&gt;You keep your browser tab (which is now sitting at a login page) open.&lt;/li&gt;&lt;li&gt;The next day you return to this browser tab and enter your username and password.&lt;/li&gt;&lt;/ol&gt;Assuming that you enter your username and password correctly, I think it is a safe assumption that the behavior you would expect is for the site to log you in. Anything else could be considered a bug.&lt;br /&gt;&lt;br /&gt;Why do I bring up such a simple scenario? &amp;nbsp;Because if the website was written using &lt;a href="http://en.wikipedia.org/wiki/JavaServer_Faces"&gt;JSF&lt;/a&gt; and they just used out of the box settings (for example what you would get if you use &lt;a href="http://docs.jboss.org/seam/latest/en-US/html/gettingstarted.html"&gt;seam-gen&lt;/a&gt;&amp;nbsp;with a &lt;a href="http://seamframework.org/"&gt;Seam&lt;/a&gt; application), the above scenario will result in an error!&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Component Web Architecture&lt;/span&gt;&lt;br /&gt;Both JSF and &lt;a href="http://www.asp.net/"&gt;ASP.NET &lt;/a&gt;use a component based web architecture. What this means is that the developer's markup language, whether it is &lt;a href="http://quickstarts.asp.net/QuickStartv20/aspnet/doc/pages/pages.aspx"&gt;aspx&lt;/a&gt;, &lt;a href="http://java.sun.com/products/jsp/"&gt;jsp&lt;/a&gt;, or &lt;a href="http://en.wikipedia.org/wiki/Facelets"&gt;facelets&lt;/a&gt;, doesn't directly define HTML. Rather it describes a tree of components. The HTML is generated after the component tree has been built and populated, either by asking the components to render themselves, or by having a separate renderer that traverses the component tree.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Advantages&lt;/span&gt;&lt;br /&gt;Component based architecture attempts to leverage the fact that almost all modern programming languages are object oriented. Components are just objects. By using a paradigm that most developers are familiar with the hope is that it'll be easy to use, maintain, extend, etc.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Problem&lt;/span&gt;&lt;br /&gt;As you know, the web is stateless. The browser and web server interact by sending messages back and forth. However, the client (browser) and server are two completely disconnected entities. The conversation between them might not actually even be between two entities. A user might save a hyperlink from one page, and then follow the link from a different browser on an entirely different computer. Because of load balancing, or server restarts, the server that handles one message in a conversation isn't necessarily the same as the one that handled the previous message. And even if its the same server, the server might be in a very different state due to handling requests from other clients or other external changes like database state changes.&lt;br /&gt;&lt;br /&gt;This presents a myriad of challenges to web developers. Web frameworks provide tools to overcome the above difficulties and still write useful applications. JSF and ASP.NET protect the developers from all of the ugliness through their use of components. Through clever use of &lt;a href="http://en.wikipedia.org/wiki/HTTP_cookie"&gt;cookies&lt;/a&gt;, &lt;a href="http://stackoverflow.com/questions/424151/viewstate-or-hiddenfield"&gt;hidden form variables&lt;/a&gt;, and &lt;a href="http://en.wikipedia.org/wiki/Session_(computer_science)"&gt;session information&lt;/a&gt;, these frameworks provide the illusion that you have objects which persist from one page request to the next and that links, form fields, and buttons change the state and or perform actions on these objects.&lt;br /&gt;&lt;br /&gt;The problem is that the long lived object model is a &lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;leaky abstraction&lt;/a&gt; over the reality of stateless messages being sent back and forth. My &lt;a href="http://madkingsmusings.blogspot.com/2010/10/why-are-web-frameworks-so-bad.html"&gt;previous complaints&lt;/a&gt; about &lt;a href="http://madkingsmusings.blogspot.com/2010/03/aspnet-updatepanel-must-be-added-before.html"&gt;ASP.NET&lt;/a&gt; are caused by trying to paper over this leak.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Component Web Mismatch&lt;/span&gt;&lt;br /&gt;To correctly process any form submission (for example login), the first thing the webserver has to do is recreate the exact same component tree structure that was used to generate the page. The appropriate components then get loaded with whatever form data data was submitted, and the component corresponding to the button that was clicked then can have its action called. The challenge is ensuring that the component tree structure that was used when the page was created is the EXACT SAME as the component structure on the form submission. For the simple cases, this is easy. But imagine that the component tree is based on a database query (e.g. listing of items on sale). It's possible that the database has changed between page load and form submission (e.g. items sold to another user). If you naively build the component tree the same way you did originally (e.g. by iterating over results from database), you will get a different component tree which can cause all types of &lt;a href="http://weblogs.asp.net/infinitiesloop/archive/2006/10/16/TRULY-Understanding-Dynamic-Controls-_2800_Part-4_2900_.aspx"&gt;wonky behavior&lt;/a&gt;. For example if the user tried to buy the 5&lt;sup&gt;th&lt;/sup&gt;&amp;nbsp;item on the list, they may end up purchasing the wrong item, since the 5&lt;sup&gt;th&lt;/sup&gt; item they saw on the screen isn't the same as the 5&lt;sup&gt;th&lt;/sup&gt;&amp;nbsp;item currently returned by the database.&lt;br /&gt;&lt;br /&gt;To solve this problem, when a form is created JSF saves the component tree so that it can be recreated when the form is submitted. This can be stored in a hidden field in the form sent to the client, but since this can be quite large it typically isn't done. By default, JSF saves this in a server session variable. However, if the session times out, this data is lost, and you can no longer recreate the state. This is the cause of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;javax.faces.application.ViewExpiredException&lt;/span&gt; that happens in the scenario at the top of this post.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Solution&lt;/span&gt;&lt;br /&gt;We need web frameworks that don't try to recreate the exact same state on two different requests. There are some very smart people who have (and continue to) work on JSF and ASP.NET. The fact that their solutions are so complex and still tend to drop people through the cracks (based on the questions out there on message boards) is indicative that the approach is flawed, rather than just the implementations. Frameworks like &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; which ease the process of writing web apps without hiding the fact that each web request is its own independent action are the long term future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-4655232007479509740?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/4655232007479509740/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=4655232007479509740' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4655232007479509740'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4655232007479509740'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/02/problem-with-component-based-web.html' title='The Problem with Component Based Web Architectures'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-4712604838349070422</id><published>2011-02-21T09:00:00.047-05:00</published><updated>2011-02-22T09:24:14.804-05:00</updated><title type='text'>Crop and Upload Image - Client Side</title><content type='html'>I want to be able to extend my &lt;a href="http://madkingsmusings.blogspot.com/2010/11/scrolling-in-javascript.html"&gt;moving icon program&lt;/a&gt;&amp;nbsp;to allow a user to upload their own icon. However, I want to give the user the ability to crop the image so they just upload the portion that they want. Here is how I do that.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Technologies Used&lt;/span&gt;&lt;br /&gt;I use the HTML 5 &lt;a href="http://html5demos.com/file-api"&gt;file capabilities&lt;/a&gt; to upload the image to the browser. I use the &lt;a href="http://code.google.com/p/jcrop/"&gt;Jcrop&lt;/a&gt; plugin to &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; to actually specify the crop region. I also leaned heavily on another demo I found on the web (&lt;a href="http://www.pandamatak.com/people/anand/blog/2010/11/clientside_image_crop_and_then.html"&gt;http://www.pandamatak.com/people/anand/blog/2010/11/clientside_image_crop_and_then.html&lt;/a&gt;) to figure out how to do it, though I have tweaked it some.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;HTML&lt;/span&gt;&lt;br /&gt;First you need a place to drag the image too.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;div&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;='cropWidget' &lt;span style="color: red;"&gt;height&lt;/span&gt;='400' &lt;span style="color: red;"&gt;width&lt;/span&gt;='400'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;div&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;='surface'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;div&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;canvas&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;='canv1' &lt;span style="color: red;"&gt;height&lt;/span&gt;='400' &lt;span style="color: red;"&gt;width&lt;/span&gt;='400'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;canvas&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;div&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;The surface is where an image can be dragged from your file system and dropped to. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;canv1&lt;/span&gt; is the canvas object that will handle image manipulation.&lt;br /&gt;&lt;br /&gt;Next we need a place to show the cropped portion of the image.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;canvas&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;='canv2' &lt;span style="color: red;"&gt;height&lt;/span&gt;='200' &lt;span style="color: red;"&gt;width&lt;/span&gt;='200'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;canvas&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;/pre&gt;&lt;pre&gt;&lt;span style="color: maroon;"&gt;&lt;/span&gt;&lt;/pre&gt;The only other portion is a control to put the content, two text controls which will show the size of the cropped image, and a button to upload the image.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;input&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;="&lt;span style="color: blue;"&gt;content64&lt;/span&gt;" &lt;span style="color: red;"&gt;name&lt;/span&gt;="&lt;span style="color: blue;"&gt;content64&lt;/span&gt;" &lt;span style="color: red;"&gt;type&lt;/span&gt;="&lt;span style="color: blue;"&gt;hidden&lt;/span&gt;" /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;div&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;='field'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;label&lt;/span&gt; &lt;span style="color: red;"&gt;for&lt;/span&gt;="&lt;span style="color: blue;"&gt;width&lt;/span&gt;"&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Width&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;label&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;br&lt;/span&gt; /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;input&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;="&lt;span style="color: blue;"&gt;width&lt;/span&gt;" &lt;span style="color: red;"&gt;name&lt;/span&gt;="&lt;span style="color: blue;"&gt;width&lt;/span&gt;" &lt;span style="color: red;"&gt;readOnly&lt;/span&gt;="&lt;span style="color: blue;"&gt;true&lt;/span&gt;" &lt;span style="color: red;"&gt;size&lt;/span&gt;="&lt;span class="Apple-style-span" style="color: blue;"&gt;5&lt;/span&gt;" &lt;span style="color: red;"&gt;type&lt;/span&gt;="&lt;span style="color: blue;"&gt;text&lt;/span&gt;" /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;div&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;div&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;='field'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;label&lt;/span&gt; &lt;span style="color: red;"&gt;for&lt;/span&gt;="&lt;span style="color: blue;"&gt;height&lt;/span&gt;"&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;Height&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;label&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;br&lt;/span&gt; /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;input&lt;/span&gt; &lt;span style="color: red;"&gt;id&lt;/span&gt;="&lt;span style="color: blue;"&gt;height&lt;/span&gt;" &lt;span style="color: red;"&gt;name&lt;/span&gt;="&lt;span style="color: blue;"&gt;height&lt;/span&gt;" &lt;span style="color: red;"&gt;readOnly&lt;/span&gt;="&lt;span style="color: blue;"&gt;true&lt;/span&gt;" &lt;span style="color: red;"&gt;size&lt;/span&gt;="&lt;span style="color: blue;"&gt;5&lt;/span&gt;" &lt;span style="color: red;"&gt;type&lt;/span&gt;="&lt;span style="color: blue;"&gt;text&lt;/span&gt;" /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;div&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;div&lt;/span&gt; &lt;span style="color: red;"&gt;class&lt;/span&gt;='actions'&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;input&lt;/span&gt; &lt;span style="color: red;"&gt;disabled&lt;/span&gt;="&lt;span style="color: blue;"&gt;disabled&lt;/span&gt;" &lt;span style="color: red;"&gt;id&lt;/span&gt;="&lt;span style="color: blue;"&gt;imageSubmit&lt;/span&gt;" &lt;span style="color: red;"&gt;name&lt;/span&gt;="&lt;span style="color: blue;"&gt;commit&lt;/span&gt;" &lt;br /&gt;         &lt;span style="color: red;"&gt;onClick&lt;/span&gt;="&lt;span style="color: blue;"&gt;uploadSelection();&lt;/span&gt;" &lt;span style="color: red;"&gt;type&lt;/span&gt;="&lt;span style="color: blue;"&gt;submit&lt;/span&gt;" &lt;span style="color: red;"&gt;value&lt;/span&gt;="&lt;span style="color: blue;"&gt;Create Image&lt;/span&gt;" /&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;div&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;There are a couple of things to note here. First the "readOnly" attribute on the text inputs. The user won't be entering the width and height, they will be calculated by JavaScript. Also, the imageSubmit button is disabled. It does not become enabled until an image has been uploaded. And then on click, the JavaScript function uploadSelection will be called.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;CSS&lt;/span&gt;&lt;br /&gt;The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;surface&lt;/span&gt; element and the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;canv1&lt;/span&gt; element need to be superimposed on top of each other for this to look right. This CSS accomplishes this, plus adds a little bit of styling.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;style&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;  #cropWidget {position:relative; width 400px; height:400px; padding:5px;} &lt;br /&gt;  #surface {position:absolute; top:0px; left: 0px; background:blue;  &lt;br /&gt;            width:400px; height:400px; z-index:100; opacity: 0.50;} &lt;br /&gt;  #canv1 {position:absolute; top:0px; left: 0px; border:1px solid blue; &lt;br /&gt;          width:400px; height:400px;} &lt;br /&gt;  #canv2 {position:relative; border:1px solid yellow; width:200px; height:200px;} &lt;br /&gt;&lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/&lt;span style="color: maroon;"&gt;style&lt;/span&gt;&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;JavaScript&lt;/span&gt;&lt;br /&gt;Here is all of the javascript. I will explain the parts below.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; cropWidget = document.getElementById(&lt;span style="color: maroon;"&gt;'cropWidget'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; surface = document.getElementById(&lt;span style="color: maroon;"&gt;'surface'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; canv1 = document.getElementById(&lt;span style="color: maroon;"&gt;'canv1'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; canv2 = document.getElementById(&lt;span style="color: maroon;"&gt;'canv2'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; imageSubmit = document.getElementById(&lt;span style="color: maroon;"&gt;'imageSubmit'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; ctx1 = canv1.getContext(&lt;span style="color: maroon;"&gt;'2d'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt; &lt;span style="color: blue;"&gt;var&lt;/span&gt; ctx2 = canv2.getContext(&lt;span style="color: maroon;"&gt;'2d'&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt; &lt;span style="color: blue;"&gt;function&lt;/span&gt; resize(comp, width, height) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;   comp.width = width; &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;   comp.height = height; &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;   comp.style.width = img.width + &lt;span style="color: maroon;"&gt;'px'&lt;/span&gt;; &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;   comp.style.height = img.height + &lt;span style="color: maroon;"&gt;'px'&lt;/span&gt;;&lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt; } &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt; &lt;span style="color: blue;"&gt;function&lt;/span&gt; displayImage(img) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 17&lt;/span&gt;   resize(canv1, img.width, img.height); &lt;br /&gt;&lt;span style="color: teal;"&gt; 18&lt;/span&gt;   resize(surface, img.width, img.height); &lt;br /&gt;&lt;span style="color: teal;"&gt; 19&lt;/span&gt;   resize(cropWidget, img); &lt;br /&gt;&lt;span style="color: teal;"&gt; 20&lt;/span&gt;   &lt;span style="color: blue;"&gt;while&lt;/span&gt;(surface.childNodes[&lt;span style="color: maroon;"&gt;0&lt;/span&gt;]) surface.removeChild(surface.childNodes[&lt;span style="color: maroon;"&gt;0&lt;/span&gt;]);&lt;br /&gt;&lt;span style="color: teal;"&gt; 21&lt;/span&gt;   surface.appendChild(img); &lt;br /&gt;&lt;span style="color: teal;"&gt; 22&lt;/span&gt;   ctx1.drawImage(img, &lt;span style="color: maroon;"&gt;0&lt;/span&gt;, &lt;span style="color: maroon;"&gt;0&lt;/span&gt;, img.width, img.height);&lt;br /&gt;&lt;span style="color: teal;"&gt; 23&lt;/span&gt;   jQuery(&lt;span style="color: maroon;"&gt;"#"&lt;/span&gt; + img.id).Jcrop({ aspectRatio: &lt;span style="color: maroon;"&gt;1&lt;/span&gt;, onSelect: cropImage });&lt;br /&gt;&lt;span style="color: teal;"&gt; 24&lt;/span&gt; } &lt;br /&gt;&lt;span style="color: teal;"&gt; 25&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 26&lt;/span&gt; &lt;span style="color: blue;"&gt;function&lt;/span&gt; loadImg(imgFile) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 27&lt;/span&gt;   &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!imgFile.type.match(/image.*/)) &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;span style="color: teal;"&gt; 28&lt;/span&gt;   &lt;span style="color: blue;"&gt;var&lt;/span&gt; img = document.createElement(&lt;span style="color: maroon;"&gt;"img"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 29&lt;/span&gt;   img.id = &lt;span style="color: maroon;"&gt;"pic"&lt;/span&gt;; &lt;br /&gt;&lt;span style="color: teal;"&gt; 30&lt;/span&gt;   img.file = imgFile; &lt;br /&gt;&lt;span style="color: teal;"&gt; 31&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 32&lt;/span&gt;   &lt;span style="color: blue;"&gt;var&lt;/span&gt; reader = &lt;span style="color: blue;"&gt;new&lt;/span&gt; FileReader();&lt;br /&gt;&lt;span style="color: teal;"&gt; 33&lt;/span&gt;   reader.onload = &lt;span style="color: blue;"&gt;function&lt;/span&gt;(e) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 34&lt;/span&gt;     img.onload = &lt;span style="color: blue;"&gt;function&lt;/span&gt;() { displayImage(img); }; &lt;br /&gt;&lt;span style="color: teal;"&gt; 35&lt;/span&gt;     img.src = e.target.result; &lt;br /&gt;&lt;span style="color: teal;"&gt; 36&lt;/span&gt;     resize(canv2, &lt;span style="color: maroon;"&gt;200&lt;/span&gt;, &lt;span style="color: maroon;"&gt;200&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: teal;"&gt; 37&lt;/span&gt;   }; &lt;br /&gt;&lt;span style="color: teal;"&gt; 38&lt;/span&gt;   reader.readAsDataURL(imgFile); &lt;br /&gt;&lt;span style="color: teal;"&gt; 39&lt;/span&gt; } &lt;br /&gt;&lt;span style="color: teal;"&gt; 40&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 41&lt;/span&gt; &lt;span style="color: blue;"&gt;function&lt;/span&gt; cropImage(c) { &lt;br /&gt;&lt;span style="color: teal;"&gt; 42&lt;/span&gt;   &lt;span style="color: blue;"&gt;var&lt;/span&gt; w = c.x2-c.x; &lt;br /&gt;&lt;span style="color: teal;"&gt; 43&lt;/span&gt;   &lt;span style="color: blue;"&gt;var&lt;/span&gt; h = c.y2-c.y; &lt;br /&gt;&lt;span style="color: teal;"&gt; 44&lt;/span&gt;   resize(canv2, w, h); &lt;br /&gt;&lt;span style="color: teal;"&gt; 45&lt;/span&gt;   ctx2.drawImage(canv1, c.x, c.y, w, h, &lt;span style="color: maroon;"&gt;0&lt;/span&gt;, &lt;span style="color: maroon;"&gt;0&lt;/span&gt;, w, h);&lt;br /&gt;&lt;span style="color: teal;"&gt; 46&lt;/span&gt;   $(&lt;span style="color: maroon;"&gt;"#width"&lt;/span&gt;).val(w); &lt;br /&gt;&lt;span style="color: teal;"&gt; 47&lt;/span&gt;   $(&lt;span style="color: maroon;"&gt;"#height"&lt;/span&gt;).val(h); &lt;br /&gt;&lt;span style="color: teal;"&gt; 48&lt;/span&gt;   imageSubmit.disabled = &lt;span style="color: maroon;"&gt;false&lt;/span&gt;; &lt;br /&gt;&lt;span style="color: teal;"&gt; 49&lt;/span&gt; } &lt;br /&gt;&lt;span style="color: teal;"&gt; 50&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 51&lt;/span&gt; &lt;span style="color: blue;"&gt;function&lt;/span&gt; uploadSelection() { &lt;br /&gt;&lt;span style="color: teal;"&gt; 52&lt;/span&gt;   &lt;span style="color: blue;"&gt;var&lt;/span&gt; imgData = document.getElementById(&lt;span style="color: maroon;"&gt;'canv2'&lt;/span&gt;).toDataURL(&lt;span style="color: maroon;"&gt;"image/png"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 53&lt;/span&gt;   $(&lt;span style="color: maroon;"&gt;"#content64"&lt;/span&gt;).val(imgData); &lt;br /&gt;&lt;span style="color: teal;"&gt; 54&lt;/span&gt;   alert(&lt;span style="color: maroon;"&gt;"This would've upload your image to a server, if there was one."&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 55&lt;/span&gt;   &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: maroon;"&gt;false&lt;/span&gt;;&lt;br /&gt;&lt;span style="color: teal;"&gt; 56&lt;/span&gt; } &lt;br /&gt;&lt;span style="color: teal;"&gt; 57&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 58&lt;/span&gt; surface.addEventListener(&lt;span style="color: maroon;"&gt;"dragover"&lt;/span&gt;, &lt;span style="color: blue;"&gt;function&lt;/span&gt;(e) {e.preventDefault();}, &lt;span style="color: maroon;"&gt;true&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 59&lt;/span&gt; surface.addEventListener(&lt;span style="color: maroon;"&gt;"drop"&lt;/span&gt;, &lt;span style="color: blue;"&gt;function&lt;/span&gt;(e) {e.preventDefault();  &lt;br /&gt;&lt;span style="color: teal;"&gt; 60&lt;/span&gt;                                            loadImg(e.dataTransfer.files[&lt;span style="color: maroon;"&gt;0&lt;/span&gt;]);},&lt;br /&gt;&lt;span style="color: teal;"&gt; 61&lt;/span&gt;                          &lt;span style="color: maroon;"&gt;true&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 62&lt;/span&gt; ctx1.fillText(&lt;span style="color: maroon;"&gt;"Drop image here"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;60&lt;/span&gt;, &lt;span style="color: maroon;"&gt;100&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 63&lt;/span&gt; ctx2.fillText(&lt;span style="color: maroon;"&gt;"Preview"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;60&lt;/span&gt;, &lt;span style="color: maroon;"&gt;100&lt;/span&gt;);&lt;/pre&gt;Lines 1-5 just create variables for the various HTML objects, and 6-7 get the drawing contexts for the canvases. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;resize&lt;/span&gt; (lines 9-14) is a helper method for changing the size of an HTML component. Lines 12-13 resize the display of the component, while lines 10-11 resize its internal structures, if the component is a canvas. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;displayImage&lt;/span&gt; (lines 16-24) first resizes the HTML components to fit the image. Then any previous image is removed from the surface. Then the image is added to the surface and drawn to the canvas. Finally &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;jcrop&lt;/span&gt; is called to enable cropping of this image, calling the function &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cropImage&lt;/span&gt; when a region is selected. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cropImage&lt;/span&gt; (lines 41-49) resizes canv2 and then draws the portion of the image onto this canvas. Then it stores the width and height into the HTML width and height components and enables the submit button.&lt;br /&gt;&lt;br /&gt;Jumping to the end, lines 62-63 put some text onto the canvases. Lines 58-61 enable the drag and drop functionality, setting &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;loadImg&lt;/span&gt; as the function that is called when a file is dropped. &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;loadImg&lt;/span&gt; (26-39) first ensures that it is actually an image being dropped. Then it creates an HTML &amp;lt;img&amp;gt; tag to contain the image. Line 32 creates an HTML5 &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;a href="http://www.w3.org/TR/FileAPI/#FileReader-interface"&gt;FileReader&lt;/a&gt;&lt;/span&gt; and line 38 reads the file that was dropped. The function defined on lines 33-37 is called when the file has been read. Line 35 sets the source of the &amp;lt;img&amp;gt; tag that was created a few lines above. Line 34 calls the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;displayImage&lt;/span&gt; function once the image has been loaded fully by the browser, and line 38 resizes the crop canvas to a fixed size of 200x200, which also has the effect of erasing any previous image drawn on it.&lt;br /&gt;&lt;br /&gt;That is basically the extent of the drag and drop functionality. The only remaining method is &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;uploadSelection&lt;/span&gt; (lines 51-56) which is called when the "Create Image" button is clicked. This function gets the base64 encoding of the cropped image and copies it to the HTML element &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;content64&lt;/span&gt; where, if this were actually connected to a server, it would be posted as part of the form back to the server.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Demo&lt;/span&gt;&lt;br /&gt;Here is what all of this looks like.&lt;br /&gt;&lt;div&gt;&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"&gt;&lt;/script&gt; &lt;script&gt;/** * Jcrop v.0.9.8 (minimized) * (c) 2008 Kelly Hallman and DeepLiquid.com * More information: http://deepliquid.com/content/Jcrop.html * Released under MIT License - this header must remain with code */(function($){$.Jcrop=function(obj,opt){var obj=obj,opt=opt;if(typeof(obj)!=='object')obj=$(obj)[0];if(typeof(opt)!=='object')opt={};if(!('trackDocument'in opt)){opt.trackDocument=$.browser.msie?false:true;if($.browser.msie&amp;&amp;$.browser.version.split('.')[0]=='8')opt.trackDocument=true;}if(!('keySupport'in opt))opt.keySupport=$.browser.msie?false:true;var defaults={trackDocument:false,baseClass:'jcrop',addClass:null,bgColor:'black',bgOpacity:.6,borderOpacity:.4,handleOpacity:.5,handlePad:5,handleSize:9,handleOffset:5,edgeMargin:14,aspectRatio:0,keySupport:true,cornerHandles:true,sideHandles:true,drawBorders:true,dragEdges:true,boxWidth:0,boxHeight:0,boundary:8,animationDelay:20,swingSpeed:3,allowSelect:true,allowMove:true,allowResize:true,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){}};var options=defaults;setOptions(opt);var $origimg=$(obj);var $img=$origimg.clone().removeAttr('id').css({position:'absolute'});$img.width($origimg.width());$img.height($origimg.height());$origimg.after($img).hide();presize($img,options.boxWidth,options.boxHeight);var boundx=$img.width(),boundy=$img.height(),$div= $( '&lt;div /&gt;' ).width(boundx).height(boundy).addClass(cssClass('holder')).css({position:'relative',backgroundColor:options.bgColor}).insertAfter($origimg).append($img);;if(options.addClass)$div.addClass(options.addClass);var $img2=$('&lt;img /&gt;' ).attr('src',$img.attr('src')).css('position','absolute').width(boundx).height(boundy);var $img_holder= $( '&lt;div /&gt;' ).width(pct(100)).height(pct(100)).css({zIndex:310,position:'absolute',overflow:'hidden'}).append($img2);var $hdl_holder=$( '&lt;div /&gt;' ).width(pct(100)).height(pct(100)).css('zIndex',320);var $sel=$( '&lt;div /&gt;' ).css({position:'absolute',zIndex:300}).insertBefore($img).append($img_holder,$hdl_holder);var bound=options.boundary;var $trk=newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)).css({position:'absolute',top:px(-bound),left:px(-bound),zIndex:290}).mousedown(newSelection);var xlimit,ylimit,xmin,ymin;var xscale,yscale,enabled=true;var docOffset=getPos($img),btndown,lastcurs,dimmed,animating,shift_down;var Coords=function(){var x1=0,y1=0,x2=0,y2=0,ox,oy;function setPressed(pos){var pos=rebound(pos);x2=x1=pos[0];y2=y1=pos[1];};function setCurrent(pos){var pos=rebound(pos);ox=pos[0]-x2;oy=pos[1]-y2;x2=pos[0];y2=pos[1];};function getOffset(){return[ox,oy];};function moveOffset(offset){var ox=offset[0],oy=offset[1];if(0&gt;x1+ox)ox-=ox+x1;if(0&gt;y1+oy)oy-=oy+y1;if(boundy&lt;y2+oy)oy+=boundy-(y2+oy);if(boundx&lt;x2+ox)ox+=boundx-(x2+ox);x1+=ox;x2+=ox;y1+=oy;y2+=oy;};function getCorner(ord){var c=getFixed();switch(ord){case'ne':return[c.x2,c.y];case'nw':return[c.x,c.y];case'se':return[c.x2,c.y2];case'sw':return[c.x,c.y2];}};function getFixed(){if(!options.aspectRatio)return getRect();var aspect=options.aspectRatio,min_x=options.minSize[0]/xscale,min_y=options.minSize[1]/yscale,max_x=options.maxSize[0]/xscale,max_y=options.maxSize[1]/yscale,rw=x2-x1,rh=y2-y1,rwa=Math.abs(rw),rha=Math.abs(rh),real_ratio=rwa/rha,xx,yy;if(max_x==0){max_x=boundx*10}if(max_y==0){max_y=boundy*10}if(real_ratio&lt;aspect){yy=y2;w=rha*aspect;xx=rw&lt;0?x1-w:w+x1;if(xx&lt;0){xx=0;h=Math.abs((xx-x1)/aspect);yy=rh&lt;0?y1-h:h+y1;}else if(xx&gt;boundx){xx=boundx;h=Math.abs((xx-x1)/aspect);yy=rh&lt;0?y1-h:h+y1;}}else{xx=x2;h=rwa/aspect;yy=rh&lt;0?y1-h:y1+h;if(yy&lt;0){yy=0;w=Math.abs((yy-y1)*aspect);xx=rw&lt;0?x1-w:w+x1;}else if(yy&gt;boundy){yy=boundy;w=Math.abs(yy-y1)*aspect;xx=rw&lt;0?x1-w:w+x1;}}if(xx&gt;x1){if(xx-x1&lt;min_x){xx=x1+min_x;}else if(xx-x1&gt;max_x){xx=x1+max_x;}if(yy&gt;y1){yy=y1+(xx-x1)/aspect;}else{yy=y1-(xx-x1)/aspect;}}else if(xx&lt;x1){if(x1-xx&lt;min_x){xx=x1-min_x}else if(x1-xx&gt;max_x){xx=x1-max_x;}if(yy&gt;y1){yy=y1+(x1-xx)/aspect;}else{yy=y1-(x1-xx)/aspect;}}if(xx&lt;0){x1-=xx;xx=0;}else if(xx&gt;boundx){x1-=xx-boundx;xx=boundx;}if(yy&lt;0){y1-=yy;yy=0;}else if(yy&gt;boundy){y1-=yy-boundy;yy=boundy;}return last=makeObj(flipCoords(x1,y1,xx,yy));};function rebound(p){if(p[0]&lt;0)p[0]=0;if(p[1]&lt;0)p[1]=0;if(p[0]&gt;boundx)p[0]=boundx;if(p[1]&gt;boundy)p[1]=boundy;return[p[0],p[1]];};function flipCoords(x1,y1,x2,y2){var xa=x1,xb=x2,ya=y1,yb=y2;if(x2&lt;x1){xa=x2;xb=x1;}if(y2&lt;y1){ya=y2;yb=y1;}return[Math.round(xa),Math.round(ya),Math.round(xb),Math.round(yb)];};function getRect(){var xsize=x2-x1;var ysize=y2-y1;if(xlimit&amp;&amp;(Math.abs(xsize)&gt;xlimit))x2=(xsize&gt;0)?(x1+xlimit):(x1-xlimit);if(ylimit&amp;&amp;(Math.abs(ysize)&gt;ylimit))y2=(ysize&gt;0)?(y1+ylimit):(y1-ylimit);if(ymin&amp;&amp;(Math.abs(ysize)&lt;ymin))y2=(ysize&gt;0)?(y1+ymin):(y1-ymin);if(xmin&amp;&amp;(Math.abs(xsize)&lt;xmin))x2=(xsize&gt;0)?(x1+xmin):(x1-xmin);if(x1&lt;0){x2-=x1;x1-=x1;}if(y1&lt;0){y2-=y1;y1-=y1;}if(x2&lt;0){x1-=x2;x2-=x2;}if(y2&lt;0){y1-=y2;y2-=y2;}if(x2&gt;boundx){var delta=x2-boundx;x1-=delta;x2-=delta;}if(y2&gt;boundy){var delta=y2-boundy;y1-=delta;y2-=delta;}if(x1&gt;boundx){var delta=x1-boundy;y2-=delta;y1-=delta;}if(y1&gt;boundy){var delta=y1-boundy;y2-=delta;y1-=delta;}return makeObj(flipCoords(x1,y1,x2,y2));};function makeObj(a){return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]};};return{flipCoords:flipCoords,setPressed:setPressed,setCurrent:setCurrent,getOffset:getOffset,moveOffset:moveOffset,getCorner:getCorner,getFixed:getFixed};}();var Selection=function(){var start,end,dragmode,awake,hdep=370;var borders={};var handle={};var seehandles=false;var hhs=options.handleOffset;if(options.drawBorders){borders={top:insertBorder('hline').css('top',$.browser.msie?px(-1):px(0)),bottom:insertBorder('hline'),left:insertBorder('vline'),right:insertBorder('vline')};}if(options.dragEdges){handle.t=insertDragbar('n');handle.b=insertDragbar('s');handle.r=insertDragbar('e');handle.l=insertDragbar('w');}options.sideHandles&amp;&amp;createHandles(['n','s','e','w']);options.cornerHandles&amp;&amp;createHandles(['sw','nw','ne','se']);function insertBorder(type){var jq=$( '&lt;div /&gt;' ).css({position:'absolute',opacity:options.borderOpacity}).addClass(cssClass(type));$img_holder.append(jq);return jq;};function dragDiv(ord,zi){var jq=$( '&lt;div /&gt;' ).mousedown(createDragger(ord)).css({cursor:ord+'-resize',position:'absolute',zIndex:zi});$hdl_holder.append(jq);return jq;};function insertHandle(ord){return dragDiv(ord,hdep++).css({top:px(-hhs+1),left:px(-hhs+1),opacity:options.handleOpacity}).addClass(cssClass('handle'));};function insertDragbar(ord){var s=options.handleSize,o=hhs,h=s,w=s,t=o,l=o;switch(ord){case'n':case's':w=pct(100);break;case'e':case'w':h=pct(100);break;}return dragDiv(ord,hdep++).width(w).height(h).css({top:px(-t+1),left:px(-l+1)});};function createHandles(li){for(i in li)handle[li[i]]=insertHandle(li[i]);};function moveHandles(c){var midvert=Math.round((c.h/2)-hhs),midhoriz=Math.round((c.w/2)-hhs),north=west=-hhs+1,east=c.w-hhs,south=c.h-hhs,x,y;'e'in handle&amp;&amp;handle.e.css({top:px(midvert),left:px(east)})&amp;&amp;handle.w.css({top:px(midvert)})&amp;&amp;handle.s.css({top:px(south),left:px(midhoriz)})&amp;&amp;handle.n.css({left:px(midhoriz)});'ne'in handle&amp;&amp;handle.ne.css({left:px(east)})&amp;&amp;handle.se.css({top:px(south),left:px(east)})&amp;&amp;handle.sw.css({top:px(south)});'b'in handle&amp;&amp;handle.b.css({top:px(south)})&amp;&amp;handle.r.css({left:px(east)});};function moveto(x,y){$img2.css({top:px(-y),left:px(-x)});$sel.css({top:px(y),left:px(x)});};function resize(w,h){$sel.width(w).height(h);};function refresh(){var c=Coords.getFixed();Coords.setPressed([c.x,c.y]);Coords.setCurrent([c.x2,c.y2]);updateVisible();};function updateVisible(){if(awake)return update();};function update(){var c=Coords.getFixed();resize(c.w,c.h);moveto(c.x,c.y);options.drawBorders&amp;&amp;borders['right'].css({left:px(c.w-1)})&amp;&amp;borders['bottom'].css({top:px(c.h-1)});seehandles&amp;&amp;moveHandles(c);awake||show();options.onChange(unscale(c));};function show(){$sel.show();$img.css('opacity',options.bgOpacity);awake=true;};function release(){disableHandles();$sel.hide();$img.css('opacity',1);awake=false;};function showHandles(){if(seehandles){moveHandles(Coords.getFixed());$hdl_holder.show();}};function enableHandles(){seehandles=true;if(options.allowResize){moveHandles(Coords.getFixed());$hdl_holder.show();return true;}};function disableHandles(){seehandles=false;$hdl_holder.hide();};function animMode(v){(animating=v)?disableHandles():enableHandles();};function done(){animMode(false);refresh();};var $track=newTracker().mousedown(createDragger('move')).css({cursor:'move',position:'absolute',zIndex:360})$img_holder.append($track);disableHandles();return{updateVisible:updateVisible,update:update,release:release,refresh:refresh,setCursor:function(cursor){$track.css('cursor',cursor);},enableHandles:enableHandles,enableOnly:function(){seehandles=true;},showHandles:showHandles,disableHandles:disableHandles,animMode:animMode,done:done};}();var Tracker=function(){var onMove=function(){},onDone=function(){},trackDoc=options.trackDocument;if(!trackDoc){$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);}function toFront(){$trk.css({zIndex:450});if(trackDoc){$(document).mousemove(trackMove).mouseup(trackUp);}}function toBack(){$trk.css({zIndex:290});if(trackDoc){$(document).unbind('mousemove',trackMove).unbind('mouseup',trackUp);}}function trackMove(e){onMove(mouseAbs(e));};function trackUp(e){e.preventDefault();e.stopPropagation();if(btndown){btndown=false;onDone(mouseAbs(e));options.onSelect(unscale(Coords.getFixed()));toBack();onMove=function(){};onDone=function(){};}return false;};function activateHandlers(move,done){btndown=true;onMove=move;onDone=done;toFront();return false;};function setCursor(t){$trk.css('cursor',t);};$img.before($trk);return{activateHandlers:activateHandlers,setCursor:setCursor};}();var KeyManager=function(){var $keymgr=$('&lt;input type="radio" /&gt;').css({position:'absolute',left:'-30px'}).keypress(parseKey).blur(onBlur),$keywrap=$( '&lt;div /&gt;' ).css({position:'absolute',overflow:'hidden'}).append($keymgr);function watchKeys(){if(options.keySupport){$keymgr.show();$keymgr.focus();}};function onBlur(e){$keymgr.hide();};function doNudge(e,x,y){if(options.allowMove){Coords.moveOffset([x,y]);Selection.updateVisible();};e.preventDefault();e.stopPropagation();};function parseKey(e){if(e.ctrlKey)return true;shift_down=e.shiftKey?true:false;var nudge=shift_down?10:1;switch(e.keyCode){case 37:doNudge(e,-nudge,0);break;case 39:doNudge(e,nudge,0);break;case 38:doNudge(e,0,-nudge);break;case 40:doNudge(e,0,nudge);break;case 27:Selection.release();break;case 9:return true;}return nothing(e);};if(options.keySupport)$keywrap.insertBefore($img);return{watchKeys:watchKeys};}();function px(n){return''+parseInt(n)+'px';};function pct(n){return''+parseInt(n)+'%';};function cssClass(cl){return options.baseClass+'-'+cl;};function getPos(obj){var pos=$(obj).offset();return[pos.left,pos.top];};function mouseAbs(e){return[(e.pageX-docOffset[0]),(e.pageY-docOffset[1])];};function myCursor(type){if(type!=lastcurs){Tracker.setCursor(type);lastcurs=type;}};function startDragMode(mode,pos){docOffset=getPos($img);Tracker.setCursor(mode=='move'?mode:mode+'-resize');if(mode=='move')return Tracker.activateHandlers(createMover(pos),doneSelect);var fc=Coords.getFixed();var opp=oppLockCorner(mode);var opc=Coords.getCorner(oppLockCorner(opp));Coords.setPressed(Coords.getCorner(opp));Coords.setCurrent(opc);Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);};function dragmodeHandler(mode,f){return function(pos){if(!options.aspectRatio)switch(mode){case'e':pos[1]=f.y2;break;case'w':pos[1]=f.y2;break;case'n':pos[0]=f.x2;break;case's':pos[0]=f.x2;break;}else switch(mode){case'e':pos[1]=f.y+1;break;case'w':pos[1]=f.y+1;break;case'n':pos[0]=f.x+1;break;case's':pos[0]=f.x+1;break;}Coords.setCurrent(pos);Selection.update();};};function createMover(pos){var lloc=pos;KeyManager.watchKeys();return function(pos){Coords.moveOffset([pos[0]-lloc[0],pos[1]-lloc[1]]);lloc=pos;Selection.update();};};function oppLockCorner(ord){switch(ord){case'n':return'sw';case's':return'nw';case'e':return'nw';case'w':return'ne';case'ne':return'sw';case'nw':return'se';case'se':return'nw';case'sw':return'ne';};};function createDragger(ord){return function(e){if(options.disabled)return false;if((ord=='move')&amp;&amp;!options.allowMove)return false;btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};};function presize($obj,w,h){var nw=$obj.width(),nh=$obj.height();if((nw&gt;w)&amp;&amp;w&gt;0){nw=w;nh=(w/$obj.width())*$obj.height();}if((nh&gt;h)&amp;&amp;h&gt;0){nh=h;nw=(h/$obj.height())*$obj.width();}xscale=$obj.width()/nw;yscale=$obj.height()/nh;$obj.width(nw).height(nh);};function unscale(c){return{x:parseInt(c.x*xscale),y:parseInt(c.y*yscale),x2:parseInt(c.x2*xscale),y2:parseInt(c.y2*yscale),w:parseInt(c.w*xscale),h:parseInt(c.h*yscale)};};function doneSelect(pos){var c=Coords.getFixed();if(c.w&gt;options.minSelect[0]&amp;&amp;c.h&gt;options.minSelect[1]){Selection.enableHandles();Selection.done();}else{Selection.release();}Tracker.setCursor(options.allowSelect?'crosshair':'default');};function newSelection(e){if(options.disabled)return false;if(!options.allowSelect)return false;btndown=true;docOffset=getPos($img);Selection.disableHandles();myCursor('crosshair');var pos=mouseAbs(e);Coords.setPressed(pos);Tracker.activateHandlers(selectDrag,doneSelect);KeyManager.watchKeys();Selection.update();e.stopPropagation();e.preventDefault();return false;};function selectDrag(pos){Coords.setCurrent(pos);Selection.update();};function newTracker(){var trk=$( '&lt;div&gt;&lt;/div&gt;' ).addClass(cssClass('tracker'));$.browser.msie&amp;&amp;trk.css({opacity:0,backgroundColor:'white'});return trk;};function animateTo(a){var x1=a[0]/xscale,y1=a[1]/yscale,x2=a[2]/xscale,y2=a[3]/yscale;if(animating)return;var animto=Coords.flipCoords(x1,y1,x2,y2);var c=Coords.getFixed();var animat=initcr=[c.x,c.y,c.x2,c.y2];var interv=options.animationDelay;var x=animat[0];var y=animat[1];var x2=animat[2];var y2=animat[3];var ix1=animto[0]-initcr[0];var iy1=animto[1]-initcr[1];var ix2=animto[2]-initcr[2];var iy2=animto[3]-initcr[3];var pcent=0;var velocity=options.swingSpeed;Selection.animMode(true);var animator=function(){return function(){pcent+=(100-pcent)/velocity;animat[0]=x+((pcent/100)*ix1);animat[1]=y+((pcent/100)*iy1);animat[2]=x2+((pcent/100)*ix2);animat[3]=y2+((pcent/100)*iy2);if(pcent&lt;100)animateStart();else Selection.done();if(pcent&gt;=99.8)pcent=100;setSelectRaw(animat);};}();function animateStart(){window.setTimeout(animator,interv);};animateStart();};function setSelect(rect){setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);};function setSelectRaw(l){Coords.setPressed([l[0],l[1]]);Coords.setCurrent([l[2],l[3]]);Selection.update();};function setOptions(opt){if(typeof(opt)!='object')opt={};options=$.extend(options,opt);if(typeof(options.onChange)!=='function')options.onChange=function(){};if(typeof(options.onSelect)!=='function')options.onSelect=function(){};};function tellSelect(){return unscale(Coords.getFixed());};function tellScaled(){return Coords.getFixed();};function setOptionsNew(opt){setOptions(opt);interfaceUpdate();};function disableCrop(){options.disabled=true;Selection.disableHandles();Selection.setCursor('default');Tracker.setCursor('default');};function enableCrop(){options.disabled=false;interfaceUpdate();};function cancelCrop(){Selection.done();Tracker.activateHandlers(null,null);};function destroy(){$div.remove();$origimg.show();};function interfaceUpdate(alt){options.allowResize?alt?Selection.enableOnly():Selection.enableHandles():Selection.disableHandles();Tracker.setCursor(options.allowSelect?'crosshair':'default');Selection.setCursor(options.allowMove?'move':'default');$div.css('backgroundColor',options.bgColor);if('setSelect'in options){setSelect(opt.setSelect);Selection.done();delete(options.setSelect);}if('trueSize'in options){xscale=options.trueSize[0]/boundx;yscale=options.trueSize[1]/boundy;}xlimit=options.maxSize[0]||0;ylimit=options.maxSize[1]||0;xmin=options.minSize[0]||0;ymin=options.minSize[1]||0;if('outerImage'in options){$img.attr('src',options.outerImage);delete(options.outerImage);}Selection.refresh();};$hdl_holder.hide();interfaceUpdate(true);var api={animateTo:animateTo,setSelect:setSelect,setOptions:setOptionsNew,tellSelect:tellSelect,tellScaled:tellScaled,disable:disableCrop,enable:enableCrop,cancel:cancelCrop,focus:KeyManager.watchKeys,getBounds:function(){return[boundx*xscale,boundy*yscale];},getWidgetSize:function(){return[boundx,boundy];},release:Selection.release,destroy:destroy};$origimg.data('Jcrop',api);return api;};$.fn.Jcrop=function(options){function attachWhenDone(from){var loadsrc=options.useImg||from.src;var img=new Image();img.onload=function(){$.Jcrop(from,options);};img.src=loadsrc;};if(typeof(options)!=='object')options={};this.each(function(){if($(this).data('Jcrop')){if(options=='api')return $(this).data('Jcrop');else $(this).data('Jcrop').setOptions(options);}else attachWhenDone(this);});return this;};})(jQuery);  &lt;/script&gt;&lt;style&gt;/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */.jcrop-holder { text-align: left; }.jcrop-vline, .jcrop-hline{ font-size: 0; position: absolute; background: white top left repeat;}.jcrop-vline { height: 100%; width: 1px !important; }.jcrop-hline { width: 100%; height: 1px !important; }.jcrop-handle { font-size: 1px; width: 7px !important; height: 7px !important; border: 1px #eee solid; background-color: #333; *width: 9px; *height: 9px;}.jcrop-tracker { width: 100%; height: 100%; }.custom .jcrop-vline,.custom .jcrop-hline{ background: yellow;}.custom .jcrop-handle{ border-color: black; background-color: #C7BB00; -moz-border-radius: 3px; -webkit-border-radius: 3px;}  &lt;/style&gt;&lt;style&gt;       #cw1cropWidget {position:relative; width 400px; height:400px; padding:5px; /*background:#aaaaaa; border:1px solid red; width: 410px; height:430px; padding:5px; overflow:none;*/}      #cw1surface {position:absolute; top:0px; left: 0px; background:blue; width:400px; height:400px; z-index:100; opacity: 0.50;}      #cw1canv1   {position:absolute; top:0px; left: 0px; border:1px solid blue;    width:400px; height:400px;}      #cw1canv2   {position:relative; border:1px solid yellow;  width:200px; height:200px;}    &lt;/style&gt; &lt;script type="text/javascript"&gt;       //&lt;![CDATA[        function cw1Init() {                  var cropWidget = document.getElementById('cw1cropWidget');          var surface = document.getElementById('cw1surface');          var canv1 = document.getElementById('cw1canv1');          var canv2 = document.getElementById('cw1canv2');          var submit = document.getElementById('cw1imageSubmit');          var ctx = canv1.getContext('2d');          var ctx2 = canv2.getContext('2d');                  function resize(comp, img) {            comp.style.width = img.width + 'px';            comp.style.height = img.height + 'px';          }                  function displayImage(img) {            canv1.width = img.width;            canv1.height = img.height;            resize(canv1, img);            resize(surface, img);            resize(cropWidget, img);            while(surface.childNodes[0]) surface.removeChild(surface.childNodes[0]);            surface.appendChild(img);            ctx.drawImage(img, 5, 5, img.width, img.height);            jQuery("#" + img.id).Jcrop({ aspectRatio: 1, onSelect: cropImage });          }                          // jcrop example (http://www.pandamatak.com/people/anand/blog/2010/11/clientside_image_crop_and_then.html)                  function loadImg(imgFile) {            if (!imgFile.type.match(/image.*/)) return;                    var img = document.createElement("img");            img.id = "pic";            img.file = imgFile;                    var reader = new FileReader();            reader.onload = function(e) {                    img.onload = function() { displayImage(img); };                    img.src = e.target.result;                    canv2.width = 199;                    canv2.width = 200;                    canv2.height = 200;                };            reader.readAsDataURL(imgFile);          }                  // Crop selection in Canvas 1 into Canvas 2          //          function cropImage(c) {            //ctx2.clearRect(0,0,200,200);            var w = c.x2-c.x;            var h = c.y2-c.y;            canv2.width = w;            canv2.height = h;            resize(canv2, canv2);            ctx2.drawImage(canv1, c.x, c.y, w, h, 0, 0, w, h);            $("#dm_image_width").val(w);            $("#dm_image_height").val(h);            imageSubmit.disabled = false;          }                  // Package the contents of canvas 2 and dispatch to server. Also need to catch the error return.          //                  surface.addEventListener("dragover", function(e) {e.preventDefault();}, true);          surface.addEventListener("drop", function(e) {e.preventDefault(); loadImg(e.dataTransfer.files[0]);}, true);          ctx.fillText("Drop image here", 60, 100);          ctx2.fillText("Preview", 60, 100);        }        function cw1uploadSelection() {          var imgData = document.getElementById('cw1canv2').toDataURL("image/png");          $("#dm_image_content64").val(imgData);    alert("This would've upload your image to a server, if there was one connected.");          return false;        }      //]]&gt;    &lt;/script&gt;&lt;br /&gt;&lt;h1&gt;Image Cropper&lt;/h1&gt;Drag and drop image here:&lt;br /&gt;&lt;div height="400" id="cw1cropWidget" width="400"&gt;&lt;div id="cw1surface"&gt;&lt;/div&gt;&lt;canvas height="400" id="cw1canv1" width="400"&gt;&lt;/canvas&gt; &lt;/div&gt;&lt;input id="dm_image_content64" name="dm_image[content64]" type="hidden" /&gt; &lt;br /&gt;Cropped image is here:&lt;br /&gt;&lt;canvas height="200" id="cw1canv2" width="200"&gt;&lt;/canvas&gt; &lt;br /&gt;&lt;div class="field"&gt;&lt;label for="dm_image_width"&gt;Width&lt;/label&gt;&lt;input id="dm_image_width" name="dm_image[width]" readonly="true" size="5" type="text" /&gt;&lt;/div&gt;&lt;div class="field"&gt;&lt;label for="dm_image_height"&gt;Height&lt;/label&gt;&lt;input id="dm_image_height" name="dm_image[height]" readonly="true" size="5" type="text" /&gt; &lt;/div&gt;&lt;div class="actions"&gt;&lt;input disabled="disabled" id="imageSubmit" name="commit" onclick="cw1uploadSelection();" type="submit" value="Create Image" /&gt; &lt;/div&gt;&lt;script&gt;   cw1Init(); &lt;/script&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-4712604838349070422?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/4712604838349070422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=4712604838349070422' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4712604838349070422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/4712604838349070422'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/02/crop-and-upload-image-client-side.html' title='Crop and Upload Image - Client Side'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6850520559063197121</id><published>2011-02-14T09:00:00.046-05:00</published><updated>2011-02-14T09:00:11.184-05:00</updated><title type='text'>Unit Testing - the Ugly</title><content type='html'>So you've weighed the &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-good.html"&gt;benefits&lt;/a&gt;&amp;nbsp;and &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-bad.html"&gt;drawbacks&lt;/a&gt;&amp;nbsp;to unit testing and decided to do some unit testing. Now you will learn a rarely talked about fact, unit testing is ugly.&lt;br /&gt;&lt;br /&gt;A unit test is composed of 3 steps:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Set up state&lt;/li&gt;&lt;li&gt;Execute component&lt;/li&gt;&lt;li&gt;Validate state&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;For any type of complex components, the code for setting up the state and validating the state can be quite large and tedious. If you look at a set of tests for a single component you'll also find that they tend to be very redundant.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To a certain extent, there is nothing you can do. Comprehensive tests, by necessity, will contain a lot of data and a lot of validation. If you are a believer in the &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY&lt;/a&gt; principle, the redundancy will drive you batty. Through standard refactoring ideas you can pull out the common setup and validation code into their own methods and helper classes. However, there are two catches.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Almost the Same&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Each of your unit tests are presumably testing different aspects of your component. As such the state will have to be different for each test. And the validation steps will be different for each test. Trying to parameterize your extracted setup and validation methods to support your scenarios can be as challenging a design problem as any portion of your application proper.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Self Contained&lt;/span&gt;&lt;/div&gt;&lt;div&gt;One goal of unit testing is that each test is self descriptive. That way when the testFrombulatorWithNull fails you can quickly narrow down the problem. Unfortunately, if you have refactored your unit tests according to the DRY principle, the setup/validation information won't be&amp;nbsp;solely&amp;nbsp;in the specific test method. You'll have to understand how the helper methods and classes interact so you can understand the test so you can understand what went wrong.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Example&lt;/span&gt;&lt;br /&gt;Imagine we modify the PhoneFormatter example from &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-bad.html"&gt;before&lt;/a&gt;&amp;nbsp;to now take a formatting string in its constructor. Now, we have a lot more cases to test, which might look something like this:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; org.junit.Assert.*;&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; org.junit.Test; &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; org.junit.Before; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; PhoneFormatterTest {&lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;   &lt;span style="color: blue;"&gt;private&lt;/span&gt; PhoneFormatter mFormatter; &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;   @Test &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; testWithSpace() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     mFormatter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneFormatter(&lt;span style="color: maroon;"&gt;"(###) ###-####"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757) 555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"7575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757) 555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"17575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"5551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 17&lt;/span&gt;   @Test &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; testNoSpace() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 18&lt;/span&gt;     mFormatter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneFormatter(&lt;span style="color: maroon;"&gt;"(###)###-####"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 19&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757)555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"7575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 20&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757)555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"17575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 21&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"5551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 22&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 23&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 24&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 25&lt;/span&gt;   @Test &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; testNoParen() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 26&lt;/span&gt;     mFormatter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneFormatter(&lt;span style="color: maroon;"&gt;"###-###-####"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 27&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"757-555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"7575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 28&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"757-555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"17575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 29&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"5551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 30&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 31&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 32&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 33&lt;/span&gt;   @Test &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; testDots() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 34&lt;/span&gt;     mFormatter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneFormatter(&lt;span style="color: maroon;"&gt;"###.###.####"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 35&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"757.555.1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"7575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 36&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"757.555.1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"17575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 37&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"555.1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"5551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 38&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"911"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 39&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 40&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 41&lt;/span&gt;   &lt;span style="color: green;"&gt;/* Test more formats here */&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 42&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 43&lt;/span&gt;   &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; check(String expected, String number) {&lt;br /&gt;&lt;span style="color: teal;"&gt; 44&lt;/span&gt;     checkImpl(expected, number); &lt;br /&gt;&lt;span style="color: teal;"&gt; 45&lt;/span&gt;     checkImpl(expected, &lt;span style="color: maroon;"&gt;"9"&lt;/span&gt; + number); &lt;span style="color: green;"&gt;// check leading 9&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 46&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 47&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 48&lt;/span&gt;   &lt;span style="color: blue;"&gt;private&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; checkImpl(String expected, String number) {&lt;br /&gt;&lt;span style="color: teal;"&gt; 49&lt;/span&gt;     PhoneNumber pn = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneNumber(number);&lt;br /&gt;&lt;span style="color: teal;"&gt; 50&lt;/span&gt;     assertEquals(expected, mFormatter.format(pn)); &lt;br /&gt;&lt;span style="color: teal;"&gt; 51&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 52&lt;/span&gt; }&lt;/pre&gt;As you can see the unit test is now a lot longer. And this is for testing a very simple class that takes a very simple input and has a very simple output. Imagine how much uglier this would be if &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PhoneFormatter&lt;/span&gt; took more complex objects as parameters to its methods and returned objects?&lt;br /&gt;&lt;br /&gt;Even with such a simple class under test there is a ton of duplication, each unit test method is almost identical to each other. This has the usual problems of cut-and-paste. For example, if you want to add international numbers to your test, you need to make sure you add this to each and every method, and it could be easy to miss one.&lt;br /&gt;&lt;br /&gt;There's also the possibility for confusion. Notice the method &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;check&lt;/span&gt; (lines 43-46) checks numbers both with and without a leading 9. A future developer, if they don't carefully follow the code, may not expect this, making it unclear why a particular test fails. In this case, it is probably worth this extra indirection to cut the size of the test class almost in half, but you are bound to come up with examples that aren't so clear.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Conclusion&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Obviously, the goal to find the happy medium where your test code is structured to minimize redundancy while still maximizing readability, just like writing any other code. However, the goals of unit testing (test component, find bugs, show example of how component is used) are not the same as writing other code. This means that you may make different decisions as part of your design trade-offs than you would elsewhere in code. When combined with the fact that comprehensive unit tests can't be concise (otherwise they wouldn't be comprehensive), it is likely that at least some of your unit tests will be ugly.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6850520559063197121?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6850520559063197121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6850520559063197121' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6850520559063197121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6850520559063197121'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/02/unit-testing-ugly.html' title='Unit Testing - the Ugly'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-6728713858477424110</id><published>2011-02-07T09:00:00.001-05:00</published><updated>2011-02-07T09:00:17.015-05:00</updated><title type='text'>Java Web Services Without Code Generation</title><content type='html'>With Java 6 it is possible to use &lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/annotations.html"&gt;annotations&lt;/a&gt; to create web services and to create clients to consume those web services without using 3rd party libraries like &lt;a href="http://ws.apache.org/axis/"&gt;Axis&lt;/a&gt;. If you have control of both the server and the client code you can easily write these pieces without any code generation tools. Here is an example of that.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Server&lt;/span&gt;&lt;br /&gt;To create your web service, perform the following steps.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a directory for the project. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;mkdir server; &amp;nbsp;cd server&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Create a package for your code. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;mkdir hello&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Create the interface for the web service. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;hello/HelloPort.java&lt;/span&gt;)&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;package&lt;/span&gt; hello; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; javax.jws.WebService; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; @WebService &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;interface&lt;/span&gt; HelloPort {&lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;   String talk(String msg); &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt; }&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Create the web service. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;hello/Hello.java&lt;/span&gt;)&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;package&lt;/span&gt; hello; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; javax.jws.WebService; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; javax.xml.ws.Endpoint; &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; @WebService &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Hello &lt;span style="color: blue;"&gt;implements&lt;/span&gt; HelloPort {&lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;   &lt;span style="color: blue;"&gt;public&lt;/span&gt; String talk(String msg) { &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;     &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: maroon;"&gt;"Hello, "&lt;/span&gt; + msg; &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;   &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; main(String[] argv) {&lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;     Endpoint.publish(&lt;span style="color: maroon;"&gt;"http://localhost:8080/Hello"&lt;/span&gt;, &lt;span style="color: blue;"&gt;new&lt;/span&gt; Hello()); &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt; }&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Compile your code. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;javac hello/Hello.java&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Run the server. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;java hello.Hello&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;/ol&gt;Now that your server is running, you can view the wsdl by going to &lt;a href="http://localhost:8080/Hello?wsdl"&gt;http://localhost:8080/Hello?wsdl&lt;/a&gt;. If you wanted to, you could've skipped the step where you created the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;HelloPort&lt;/span&gt; interface (and had &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Hello&lt;/span&gt; not implement any interface), but we will use &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;HelloPort&lt;/span&gt; in the client.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Client&lt;/span&gt;&lt;br /&gt;To create a client that consumes this web service:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a directory for the project. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;mkdir client; &amp;nbsp;cd client&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Create a directory for the Hello interface. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;mkdir hello&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Copy the interface. &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;cp ../server/hello/HelloPort.class hello&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Create the client code (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Client.java&lt;/span&gt;)&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; java.net.URL; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; javax.xml.namespace.QName; &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; javax.xml.ws.Service; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; hello.HelloPort; &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; Client {&lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;   &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; HelloService &lt;span style="color: blue;"&gt;extends&lt;/span&gt; Service {&lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;     &lt;span style="color: blue;"&gt;public&lt;/span&gt; HelloService() &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {&lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;       &lt;span style="color: blue;"&gt;super&lt;/span&gt;(&lt;span style="color: blue;"&gt;new&lt;/span&gt; URL(&lt;span style="color: maroon;"&gt;"http://localhost:8080/Hello?wsdl"&lt;/span&gt;), &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;             &lt;span style="color: blue;"&gt;new&lt;/span&gt; QName(&lt;span style="color: maroon;"&gt;"http://hello/"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"HelloService"&lt;/span&gt;)); &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;     } &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;     &lt;span style="color: blue;"&gt;public&lt;/span&gt; HelloPort getHelloPort() { &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;       &lt;span style="color: blue;"&gt;return&lt;/span&gt; &lt;span style="color: blue;"&gt;super&lt;/span&gt;.getPort(&lt;span style="color: blue;"&gt;new&lt;/span&gt; QName(&lt;span style="color: maroon;"&gt;"http://hello/"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"HelloPort"&lt;/span&gt;), &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;                            HelloPort.&lt;span style="color: blue;"&gt;class&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt;     } &lt;br /&gt;&lt;span style="color: teal;"&gt; 17&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 18&lt;/span&gt;    &lt;br /&gt;&lt;span style="color: teal;"&gt; 19&lt;/span&gt;   &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; main(String[] args) &lt;span style="color: blue;"&gt;throws&lt;/span&gt; Exception {    &lt;br /&gt;&lt;span style="color: teal;"&gt; 20&lt;/span&gt;     HelloService hs = &lt;span style="color: blue;"&gt;new&lt;/span&gt; HelloService(); &lt;br /&gt;&lt;span style="color: teal;"&gt; 21&lt;/span&gt;     HelloPort port = hs.getHelloPort(); &lt;br /&gt;&lt;span style="color: teal;"&gt; 22&lt;/span&gt;     System.out.printf(&lt;span style="color: maroon;"&gt;"ws returns [%s]%n"&lt;/span&gt;, port.talk(args[&lt;span style="color: maroon;"&gt;0&lt;/span&gt;])); &lt;br /&gt;&lt;span style="color: teal;"&gt; 23&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 24&lt;/span&gt; }&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Compile the client &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;javac Client.java&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;Run the program &amp;nbsp;(&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;java Client &amp;lt;message&amp;gt;&lt;/b&gt;&lt;/span&gt;) &amp;nbsp;or&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt; java -Djava.util.logging.config.file=log.prop Client&amp;nbsp;&amp;lt;message&amp;gt;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;And just like that you have written both a client and a server which talk to each other via &lt;a href="http://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt;.&amp;nbsp;The secondary run line (with the&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt; -Djava.util...&lt;/span&gt; option) is there in case you don't want to see the logging messages that get printed by default when you run the client.&lt;br /&gt;&lt;br /&gt;The URL specified on line 10 is the same URL that you specified on line 13 of the server up above, except with a "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;?wsdl"&lt;/span&gt; parameter. The QName defined on line 11 comes directly from the WSDL. If you look at the wsdl,&amp;nbsp;&lt;a href="http://localhost:8080/Hello?wsdl"&gt;http://localhost:8080/Hello?wsdl&lt;/a&gt;, it'll look something like:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: maroon;"&gt;xml&lt;/span&gt; &lt;span style="color: red;"&gt;version&lt;/span&gt;="&lt;span style="color: blue;"&gt;1.0&lt;/span&gt;" &lt;span style="color: red;"&gt;encoding&lt;/span&gt;="&lt;span style="color: blue;"&gt;UTF-8&lt;/span&gt;"&lt;span style="color: blue;"&gt;?&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;!--  &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt;   Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt; --&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;!--  &lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;   Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt; --&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon;"&gt;definitions&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;   &lt;span style="color: red;"&gt;xmlns:soap&lt;/span&gt;="&lt;span style="color: blue;"&gt;http://schemas.xmlsoap.org/wsdl/soap/&lt;/span&gt;" &lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;   &lt;span style="color: red;"&gt;xmlns:tns&lt;/span&gt;="&lt;span style="color: blue;"&gt;http://hello/&lt;/span&gt;" &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;   &lt;span style="color: red;"&gt;xmlns:xsd&lt;/span&gt;="&lt;span style="color: blue;"&gt;http://www.w3.org/2001/XMLSchema&lt;/span&gt;" &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;   &lt;span style="color: red;"&gt;xmlns&lt;/span&gt;="&lt;span style="color: blue;"&gt;http://schemas.xmlsoap.org/wsdl/&lt;/span&gt;" &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;   &lt;span style="color: red;"&gt;targetNamespace&lt;/span&gt;="&lt;span style="color: blue;"&gt;http://hello/&lt;/span&gt;" &lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;   &lt;span style="color: red;"&gt;name&lt;/span&gt;="&lt;span style="color: blue;"&gt;HelloService&lt;/span&gt;"&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;   &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;!-- service definition here --&lt;span style="color: blue;"&gt;&amp;gt;&lt;/span&gt; &lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt; &lt;span style="color: blue;"&gt;&amp;lt;&lt;/span&gt;/definitions&lt;/pre&gt;The targetNamespace and the name (lines 13 and 14 above) correspond to the QName used in the client.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Final Thoughts&lt;/span&gt;&lt;/div&gt;&lt;div&gt;This example was for a very specific case where you control both the client and server code and want it to be standalone. Of course, your web service needs may not match this model. If you already have a web container (&lt;a href="http://tomcat.apache.org/"&gt;Tomcat&lt;/a&gt;, etc), you can always deploy the Hello web service in that, rather than running it as a standalone application. And, of course, you could write your client in any language, as long as it conforms to the WSDL. Or you can just give customers the WSDL and let them write their own client.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;If you have to write a client and don't have handy interface classes (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;hello.HelloPort&lt;/span&gt; in the above example), you can do that as well. You'll just have to generate classes using the &lt;a href="http://download.oracle.com/javase/6/docs/technotes/tools/share/wsimport.html"&gt;wsimport&lt;/a&gt; command which comes with Java, but that is beyond the scope of this particular blog post.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5724099066106449846-6728713858477424110?l=madkingsmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://madkingsmusings.blogspot.com/feeds/6728713858477424110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5724099066106449846&amp;postID=6728713858477424110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6728713858477424110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5724099066106449846/posts/default/6728713858477424110'/><link rel='alternate' type='text/html' href='http://madkingsmusings.blogspot.com/2011/02/java-web-services-without-code.html' title='Java Web Services Without Code Generation'/><author><name>Michael Haddox-Schatz</name><uri>http://www.blogger.com/profile/02782657000665338948</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5724099066106449846.post-629193705945542372</id><published>2011-01-31T09:00:00.033-05:00</published><updated>2011-01-31T09:00:25.903-05:00</updated><title type='text'>Unit Testing - the Bad</title><content type='html'>&lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-good.html"&gt;Previously&lt;/a&gt;&amp;nbsp;I talked about some of the benefits of unit testing. However, it's not all peaches and cream. There are costs to unit testing.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Time&lt;/span&gt;&lt;br /&gt;The most obvious cost is the time taken to write the unit tests. If you are trying to test a component which manipulates very complex objects, you first have to generate these complex objects and then you have to validate them after the fact. If you are trying to test a component that is being used in a multi-threaded environment you have to create a mock environment with multiple threads. In these types of scenarios it can take significantly more time and effort to write meaningful unit tests than it did to write the actual component in the first place. The testing environment can be complex enough that it'll also take more time to debug the tests than it does to debug the code.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Dependencies&lt;/span&gt;&lt;br /&gt;If you are writing unit tests, you are probably using a framework like &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; or &lt;a href="http://testng.org/"&gt;TestNG&lt;/a&gt;. You may also be using additional tools to help reduce some of the complexity discussed in the previous paragraph. However, each of these tools adds a dependency. It's frustrating enough when you can't build a project because you are missing a library. Not being able to build because the tests are missing a library is worse. This can lead to developers just deleting/commenting/ignoring tests so they can go forward, meaning all that time you spent is wasted.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Inertia&lt;/span&gt;&lt;br /&gt;One of the biggest challenges to developing software is maintaining code as requirements change. A promise of automated unit tests is that they allow you to refactor code without fear of breaking things. However, a large enough suite of unit tests can actually make it more work to restructure code. When you decide that you need to completely restructure entire packages, destroying classes, and creating new ones, there's obviously a lot of work in modifying the rest of the code to be consistent with these changes. Unit tests can be a large part of your code base, and with large structural changes they will have to also be drastically changed to appropriately test the new code. While logically this is no different than the cost of writing the code and unit tests to start with, psychologically it is different. This added work can encourage you to just tack on changes in the most expedient way. i.e. A large suite of unit tests can actually cause the exact opposite effect that is desired, making you less likely to make valuable architectural changes rather than more likely.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Example&lt;/span&gt;&lt;br /&gt;Imagine that you have modified your &lt;a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-good.html"&gt;phone number code from before&lt;/a&gt;&amp;nbsp;so that phone numbers are passed around as &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PhoneNumber&lt;/span&gt; objects rather than as &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Strings&lt;/span&gt;. To work with this you modify the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PhoneFormatter&lt;/span&gt;'s &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;format&lt;/span&gt; method to take a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PhoneNumber&lt;/span&gt; object rather than a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;String&lt;/span&gt;. Well, now all of your &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PhoneFormatter&lt;/span&gt; unit tests have broken. While this particular example isn't too much work to fix, especially if you refactor the test code first, it's still extra work. I'm sure you can imagine how larger changes on more complex code could require major reworking of unit tests.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: teal;"&gt;  1&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; &lt;span style="color: blue;"&gt;static&lt;/span&gt; org.junit.Assert.*;&lt;br /&gt;&lt;span style="color: teal;"&gt;  2&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; org.junit.Test; &lt;br /&gt;&lt;span style="color: teal;"&gt;  3&lt;/span&gt; &lt;span style="color: blue;"&gt;import&lt;/span&gt; org.junit.Before; &lt;br /&gt;&lt;span style="color: teal;"&gt;  4&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  5&lt;/span&gt; &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;class&lt;/span&gt; PhoneFormatterTest {&lt;br /&gt;&lt;span style="color: teal;"&gt;  6&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  7&lt;/span&gt;   &lt;span style="color: blue;"&gt;private&lt;/span&gt; PhoneFormatter mFormatter; &lt;br /&gt;&lt;span style="color: teal;"&gt;  8&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt;  9&lt;/span&gt;   @Before &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; setup() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 10&lt;/span&gt;     mFormatter = &lt;span style="color: blue;"&gt;new&lt;/span&gt; PhoneFormatter(); &lt;br /&gt;&lt;span style="color: teal;"&gt; 11&lt;/span&gt;   } &lt;br /&gt;&lt;span style="color: teal;"&gt; 12&lt;/span&gt;  &lt;br /&gt;&lt;span style="color: teal;"&gt; 13&lt;/span&gt;   @Test &lt;span style="color: blue;"&gt;public&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; testDirectNumber() {&lt;br /&gt;&lt;span style="color: teal;"&gt; 14&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757) 555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"7575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 15&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"(757) 555-1234"&lt;/span&gt;, &lt;span style="color: maroon;"&gt;"17575551234"&lt;/span&gt;); &lt;br /&gt;&lt;span style="color: teal;"&gt; 16&lt;/span&gt;     check(&lt;span style="color: maroon;"&gt;"555-1234"&lt;/span&gt;, &lt;span style="color: maroon;
