tag:blogger.com,1999:blog-57240990661064498462024-03-13T16:05:49.217-04:00Madking's MusingsA forum to discuss thoughts, experiences, problems, complaints, questions, and findings in the field of software engineering.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.comBlogger92125tag:blogger.com,1999:blog-5724099066106449846.post-16170007316537356512012-10-29T09:00:00.000-04:002012-10-29T09:00:08.199-04:00The Measure of a DesignWe all want to write good software rather than bad software. What makes software good? I am not going to try and answer that question today. Instead I am going to talk about something that I've observed over and over in my software career - How can you judge whether software is designed well?<br />
<br />
<span class="pullout">how easy is it to change the software?</span>Note, I am not talking about whether the software is good or not. The ultimate metric of that is, does it solve the problem it is supposed to. My question is, is the architecture and design good? The empirical measure of that is, when new requirements come up, how easy is it to change the software?<br />
<br />
Ease of changing software consists of various factors: how much code has to be written/changed, how easy is it to find the appropriate place(s) to change the code, and how confident can you be that the changes didn't break other functionality.<br />
<br />
<span style="font-size: large;">How Much Code</span><br />
This measure isn't about absolute lines of code. It's about lines of code relative to expectations. If you are adding complex functionality, you'd expect it to be a decent amount of code. On the other hand, if you are just fixing a typo in an output, you'd expect that to be a really small amount of work.<br />
<br />
There was one project I worked on that included a report of employees and their salaries. A request came in that the report have the employees sorted by salary. My expectation for this change is it should just be a matter of putting a sort call in the appropriate place, and possibly having to write a custom <a href="http://docs.oracle.com/javase/6/docs/api/java/util/Comparator.html">comparator</a>. I.e. a really quick change. In reality, this change required some major reworking, and had to be delayed because it was going to take too long to accomplish.<br />
<br />
When simple changes take a lot of work, this is evidence that your design is less than ideal.<br />
<br />
<span style="font-size: large;">Appropriate Place</span><br />
When writing software, most tasks can be accomplished in a myriad of ways. A good architecture will lead you to choose one way over another. When the architecture guides you to a specific place for putting in new features, this is a good thing. Not only does it help with consistency, it means that when your new feature has to be changed, the next developer will know exactly where to go to find what you did, and know where to make the change.<br />
<br />
This is something I really like about <a href="http://rubyonrails.org/">Ruby on Rails</a>. It is an <a href="http://stackoverflow.com/questions/802050/what-is-opinionated-software">opinionated framework</a> which sometimes feels very limiting, but means that most times I know exactly where to go find the code that I am looking for.<br />
<br />
<span style="font-size: large;">Unexpected Changes</span><br />
One fear that is common when changing existing software is that you'll break something unintentionally. A hallmark of a good design is that this rarely happens. Well structured code with clean decoupled interfaces can go a long way to giving you confidence that a new change won't have unintended consequences. Yes, having a large suite of automated tests is really helpful (whether or not your design is good), but no test suite is completely thorough. I find that when I am making changes to code, I usually have a pretty good feel of how risky the change is. When most changes feel risky, this is a sign that the design can be improved.<br />
<br />
<span style="font-size: large;">Example</span><br />
A coworker recently came to me with a change request from our client. We have an electronic form system. Form instances are allowed to be edited by the form submitter. Well, this one form type includes a list of people (as a field in the form) that the client wanted to also be allowed to edit the form. I.e. we had to make a change in the security system, but just for part of the application.<br />
<br />
To fix this, what we had to do was to override the <b>can_edit?</b> method that is in the base form class, with the logic required in the derived class. Something like:<br />
<pre style="background-color: white;"><pre><span style="color: maroon; font-weight: bold;">class</span> SpecificForm < BaseForm
<span style="color: maroon; font-weight: bold;">def</span> can_edit?
<span style="color: maroon; font-weight: bold;">super</span> || form_editors<span style="color: #808030;">.</span>include?<span style="color: #808030;">(</span>current_user<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">end</span>
<span style="color: maroon; font-weight: bold;">end</span></pre>
</pre>
The current design was a good one for this change by the above metrics. The new code required was very simple, it was obvious that "<b>can_edit?</b>" was the appropriate place to put the new logic, and since the change exists as a new method only in the one specific class (i.e. the base class's logic didn't change), it is unlikely to affect any other portion of the application.<br />
<br />
<span style="font-size: large;">Refactoring</span><br />
Code is rarely written in ideal situations. Requirements aren't all known up front. You don't always have an adequate amount of time to do a good job. As a result, existing code bases don't have the best design. When you find that more and more of your changes take more time than expected, or it isn't obvious how they should be done, or each change introduces new bugs, it is time to think about refactoring.<br />
<br />
It can be hard to find the time to do this, because by <a href="http://refactoring.com/">definition</a> refactoring doesn't add any new functionality or fix any existing bugs. Yet it is important to do at times. A month ago, the "can_edit?" change mentioned above wouldn't have been nearly so simple. The permissions functionality had been spread across multiple classes and methods and instance variables in a very inconsistant manner. As such, it was easy to get confused as to the right way to do things, easy to miss a change that needed to be made, and easy to introduce bugs. However, a couple of weeks ago, I finally got fed up with all these (and many other) problems, and refactored the code. While that meant a couple of days where nothing new got fixed or added, that time will be more than made up over the next couple months as new features and bug fixes get made much more quickly.<br />
<br />
One last thought. A, often overlooked, key to successful refactoring is pride - don't have too much pride in your code. Many times I've seen developers who are happy to refactor other people's code, but don't want to admit that there could be anything less than perfect about their own design and so resist any changes. Almost everything can be improved. Have enough security in yourself to allow your own designs to be improved and you will become a better developer.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-45379690452089441462012-07-02T09:00:00.000-04:002012-07-02T09:00:00.452-04:00Simple Spaceship in HTML 5Today's post extends <a href="http://madkingsmusings.blogspot.com/2012/06/going-in-circles-via-html5.html">last week's orbit</a> by allowing you to control the spaceship.<br />
<br />
<span style="font-size: x-large;">Demo</span><br />
<br />
<div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript">
</script>
<script>
var ns3 = {
namespace : function(name, func) {
var parts = name.split(".");
var space = this;
for (var i in parts) {
var part = parts[i];
var next = space[part];
if (!next) next = {};
next.namespace = this.namespace;
space[part] = next;
space = next;
}
if (func) {
func.call(space);
}
return space;
}
};
ns3.namespace("space", function() {
var Vector = function(x, y) {
if (arguments.length == 0) {
x = 0.0;
y = 0.0;
}
this.x = x;
this.y = y;
};
var circle = function(ctx, x, y, rad) {
ctx.beginPath();
ctx.arc(x, y, rad, 0, 2*Math.PI, false);
ctx.fill();
}
Vector.prototype.adjust = function(point, mult) {
if (arguments.length < 2) mult = 1;
this.x += point.x * mult;
this.y += point.y * mult;
}
var Ship = function() {
this.pos = new Vector();
this.vel = new Vector();
this.acc = new Vector();
this.color = "255,255,255";
this.dir = 0;
this.max_dir = 12;
this.thrusterOn = false;
this.thrusterForce = 0.1;
this.size = 0.01;
}
Ship.prototype.turnRight = function() {
this.dir = (this.dir + 1) % this.max_dir;
}
Ship.prototype.turnLeft = function() {
this.dir = (this.dir + this.max_dir - 1) % this.max_dir;
}
Ship.prototype.angle = function() {
return this.dir * 2 * Math.PI / this.max_dir
}
Ship.prototype.adjust = function(time) {
if (this.thrusterOn) {
this.acc.x += this.thrusterForce * Math.sin(this.angle());
this.acc.y -= this.thrusterForce * Math.cos(this.angle());
}
this.vel.adjust(this.acc, time);
this.pos.adjust(this.vel, time);
}
Ship.prototype.draw = function(ctx) {
ctx.save();
ctx.translate(this.pos.x, this.pos.y);
ctx.rotate(this.angle());
ctx.strokeStyle = "rgb(" + this.color + ")";
ctx.scale(this.size, this.size);
ctx.beginPath();
ctx.moveTo(0, -10);
ctx.lineTo(5, 10);
ctx.lineTo(-5, 10);
ctx.lineTo(0, -10);
ctx.stroke();
if (this.thrusterOn) {
ctx.fillStyle = "rgb(" + this.color + ")";
circle(ctx, 0, 11, 1);
circle(ctx, -2, 13, 1);
circle(ctx, 2, 13, 1);
circle(ctx, -4, 15, 1);
circle(ctx, 4, 15, 1);
circle(ctx, -1, 15, 1);
circle(ctx, 1, 15, 1);
}
ctx.restore();
}
var Simulation = function(ctx, size) {
this.ctx = ctx;
this.size = size;
this.reset();
}
Simulation.prototype.drawStar = function() {
this.ctx.fillStyle= "#FF0";
circle(this.ctx, 0, 0, 20);
}
Simulation.prototype.draw = function() {
this.ctx.fillStyle = "rgba(0,0,0,1.0)";
this.ctx.fillRect(0, 0, this.size, this.size);
var dim = this.size/2;
this.ctx.save();
this.ctx.translate(dim, dim);
this.drawStar();
this.ctx.scale(dim/2.0, dim/2.0);
this.ship.draw(this.ctx);
this.ctx.restore();
}
Simulation.prototype.step = function() {
var ship = this.ship;
var acc = ship.acc;
var angle = Math.atan2(ship.pos.x, ship.pos.y);
var d2 = ship.pos.x*ship.pos.x + ship.pos.y*ship.pos.y;
acc.x = -Math.sin(angle) / d2;
acc.y = -Math.cos(angle) / d2;
ship.adjust(this.mult);
if (d2 > 16) {
this.stop();
alert("Ship Left Orbit!");
}
}
Simulation.prototype.reset = function() {
this.stop();
var ship = new Ship();
ship.pos.x = 1.0;
ship.pos.y = 0.0;
ship.vel.x = 0.0;
ship.vel.y = -1.0;
this.ship = ship;
}
Simulation.prototype.run = function(mult, refresh) {
//alert("run");
this.stop();
this.mult = mult;
var self = this;
this.timer = setInterval(function() {
self.step();
self.draw();
}, refresh);
}
Simulation.prototype.stop = function() {
//alert("STOP!");
clearInterval(this.timer);
}
this.Simulation = Simulation;
});
$(function() {
var canvas = document.getElementById("space3Canvas");
var size = 500;
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
var sim = new ns3.space.Simulation(ctx, size);
sim.draw();
$("#space3Run").click(function() {sim.run($("#space3Mult").val(), $("#space3Refresh").val())});
$("#space3Stop").click(function() {sim.stop()});
$("#space3Reset").click(function() {sim.reset(); sim.draw();});
$("#space3Ccw").click(function() {sim.ship.turnLeft(); sim.draw();});
$("#space3Cw").click(function() {sim.ship.turnRight(); sim.draw();});
$("#space3Thruster").mousedown(function() {sim.ship.thrusterOn = true; sim.draw();});
$("#space3Thruster").mouseup(function() {sim.ship.thrusterOn = false; sim.draw();});
});
</script>
<canvas id="space3Canvas"></canvas>
<br />
Mult: <input id="space3Mult" size="4" type="text" value="0.01" />
Refresh: <input id="space3Refresh" size="3" type="text" value="15" />ms
<button id="space3Run">Run</button>
<button id="space3Stop">Stop</button>
<button id="space3Reset">Reset</button>
<br />
<button id="space3Ccw">CCW</button>
<button id="space3Cw">CW</button>
<button id="space3Thruster">Thruster</button>
</div>
<span style="font-size: x-large;"><br /></span><br />
<span style="font-size: x-large;">JavaScript</span><br />
This code uses the same 3 classes as the <a href="http://madkingsmusings.blogspot.com/2012/06/going-in-circles-via-html5.html">previous demo</a>. To keep the math simpler, I assume that the spaceship starts out 1 unit from the center of the solar system and has a speed of 1. Unlike the previous demo, the acceleration follows the <a href="http://en.wikipedia.org/wiki/Inverse-square_law">inverse square law</a> to improve realism.<br />
<br />
<span style="font-size: large;">Ship</span><br />
The ship is still a position, velocity, and acceleration, but now it also has a direction and a concept of a thruster.<br />
<pre style="text-align: left;"><span style="color: teal;"> </span><span style="background-color: white; color: teal;">1</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> Ship = </span><span style="background-color: white; color: blue;">function</span><span style="background-color: white;">() {</span>
<span style="color: teal;"> 2</span> <span style="color: blue;">this</span>.pos = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 3</span> <span style="color: blue;">this</span>.vel = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 4</span> <span style="color: blue;">this</span>.acc = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 5</span> <span style="color: blue;">this</span>.color = <span style="color: maroon;">"255,255,255"</span>;
<span style="color: teal;"> 6</span> <span style="color: blue;">this</span>.dir = <span style="color: maroon;">0</span>;
<span style="color: teal;"> 7</span> <span style="color: blue;">this</span>.max_dir = <span style="color: maroon;">12</span>;
<span style="color: teal;"> 8</span> <span style="color: blue;">this</span>.thrusterOn = <span style="color: maroon;">false</span>;
<span style="color: teal;"> 9</span> <span style="color: blue;">this</span>.thrusterForce = <span style="color: maroon;">0</span><span style="color: maroon;">.1</span>;
<span style="color: teal;"> 10</span> <span style="color: blue;">this</span>.size = <span style="color: maroon;">0</span><span style="color: maroon;">.01</span>;
<span style="color: teal;"> 11</span> }
<span style="color: teal;"> 12</span>
<span style="color: teal;"> 13</span> Ship.prototype.turnRight = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 14</span> <span style="color: blue;">this</span>.dir = (<span style="color: blue;">this</span>.dir + <span style="color: maroon;">1</span>) % <span style="color: blue;">this</span>.max_dir;
<span style="color: teal;"> 15</span> }
<span style="color: teal;"> 16</span>
<span style="color: teal;"> 17</span> Ship.prototype.turnLeft = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 18</span> <span style="color: blue;">this</span>.dir = (<span style="color: blue;">this</span>.dir + <span style="color: blue;">this</span>.max_dir - <span style="color: maroon;">1</span>) % <span style="color: blue;">this</span>.max_dir;
<span style="color: teal;"> 19</span> }
<span style="color: teal;"> 20</span>
<span style="color: teal;"> 21</span> Ship.prototype.angle = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 22</span> <span style="color: blue;">return</span> <span style="color: blue;">this</span>.dir * <span style="color: maroon;">2</span> * Math.PI / <span style="color: blue;">this</span>.max_dir
<span style="color: teal;"> 23</span> }
<span style="color: teal;"> 24</span>
<span style="color: teal;"> 25</span> Ship.prototype.adjust = <span style="color: blue;">function</span>(time) {
<span style="color: teal;"> 26</span> <span style="color: blue;">if</span> (<span style="color: blue;">this</span>.thrusterOn) {
<span style="color: teal;"> 27</span> <span style="color: blue;">this</span>.acc.x += <span style="color: blue;">this</span>.thrusterForce * Math.sin(<span style="color: blue;">this</span>.angle());
<span style="color: teal;"> 28</span> <span style="color: blue;">this</span>.acc.y -= <span style="color: blue;">this</span>.thrusterForce * Math.cos(<span style="color: blue;">this</span>.angle());
<span style="color: teal;"> 29</span> }
<span style="color: teal;"> 30</span> <span style="color: blue;">this</span>.vel.adjust(<span style="color: blue;">this</span>.acc, time);
<span style="color: teal;"> 31</span> <span style="color: blue;">this</span>.pos.adjust(<span style="color: blue;">this</span>.vel, time);
<span style="color: teal;"> 32</span> }
<span style="color: teal;"> 33</span>
<span style="color: teal;"> 34</span> Ship.prototype.draw = <span style="color: blue;">function</span>(ctx) {
<span style="color: teal;"> 35</span> ctx.save();
<span style="color: teal;"> 36</span> ctx.translate(<span style="color: blue;">this</span>.pos.x, <span style="color: blue;">this</span>.pos.y);
<span style="color: teal;"> 37</span> ctx.rotate(<span style="color: blue;">this</span>.angle());
<span style="color: teal;"> 38</span> ctx.strokeStyle = <span style="color: maroon;">"rgb("</span> + <span style="color: blue;">this</span>.color + <span style="color: maroon;">")"</span>;
<span style="color: teal;"> 39</span> ctx.scale(<span style="color: blue;">this</span>.size, <span style="color: blue;">this</span>.size);
<span style="color: teal;"> 40</span> ctx.beginPath();
<span style="color: teal;"> 41</span> ctx.moveTo(<span style="color: maroon;">0</span>, -<span style="color: maroon;">10</span>);
<span style="color: teal;"> 42</span> ctx.lineTo(<span style="color: maroon;">5</span>, <span style="color: maroon;">10</span>);
<span style="color: teal;"> 43</span> ctx.lineTo(-<span style="color: maroon;">5</span>, <span style="color: maroon;">10</span>);
<span style="color: teal;"> 44</span> ctx.lineTo(<span style="color: maroon;">0</span>, -<span style="color: maroon;">10</span>);
<span style="color: teal;"> 45</span> ctx.stroke();
<span style="color: teal;"> 46</span>
<span style="color: teal;"> 47</span> <span style="color: blue;">if</span> (<span style="color: blue;">this</span>.thrusterOn) {
<span style="color: teal;"> 48</span> ctx.fillStyle = <span style="color: maroon;">"rgb("</span> + <span style="color: blue;">this</span>.color + <span style="color: maroon;">")"</span>;
<span style="color: teal;"> 49</span> circle(ctx, <span style="color: maroon;">0</span>, <span style="color: maroon;">11</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 50</span> circle(ctx, -<span style="color: maroon;">2</span>, <span style="color: maroon;">13</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 51</span> circle(ctx, <span style="color: maroon;">2</span>, <span style="color: maroon;">13</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 52</span> circle(ctx, -<span style="color: maroon;">4</span>, <span style="color: maroon;">15</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 53</span> circle(ctx, <span style="color: maroon;">4</span>, <span style="color: maroon;">15</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 54</span> circle(ctx, -<span style="color: maroon;">1</span>, <span style="color: maroon;">15</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 55</span> circle(ctx, <span style="color: maroon;">1</span>, <span style="color: maroon;">15</span>, <span style="color: maroon;">1</span>);
<span style="color: teal;"> 56</span> }
<span style="color: teal;"> 57</span>
<span style="color: teal;"> 58</span> ctx.restore();
<span style="color: teal;"> 59</span> }</pre>
<span style="font-size: large;">Simulation</span><br />
The simulation has the same responsibilities as before, but also scales the display so that a radius of 1 in ship space corresponds to halfway out on the canvas.<br />
<pre style="text-align: left;"><span style="color: teal;"> </span><span style="background-color: white; color: teal;">1</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> Simulation = </span><span style="background-color: white; color: blue;">function</span><span style="background-color: white;">(ctx, size) {</span>
<span style="background-color: white; color: teal;"> 2</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">this</span><span style="background-color: white;">.ctx = ctx;</span>
<span style="color: teal;"> 3</span> <span style="color: blue;">this</span>.size = size;
<span style="color: teal;"> 4</span> <span style="color: blue;">this</span>.reset();
<span style="color: teal;"> 5</span> }
<span style="color: teal;"> 6</span>
<span style="color: teal;"> 7</span> Simulation.prototype.drawStar = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 8</span> <span style="color: blue;">this</span>.ctx.fillStyle= <span style="color: maroon;">"#FF0"</span>;
<span style="color: teal;"> 9</span> circle(<span style="color: blue;">this</span>.ctx, <span style="color: maroon;">0</span>, <span style="color: maroon;">0</span>, <span style="color: maroon;">20</span>);
<span style="color: teal;"> 10</span> }
<span style="color: teal;"> 11</span>
<span style="color: teal;"> 12</span> Simulation.prototype.draw = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 13</span> <span style="color: blue;">this</span>.ctx.fillStyle = <span style="color: maroon;">"rgba(0,0,0,1.0)"</span>;
<span style="color: teal;"> 14</span> <span style="color: blue;">this</span>.ctx.fillRect(<span style="color: maroon;">0</span>, <span style="color: maroon;">0</span>, <span style="color: blue;">this</span>.size, <span style="color: blue;">this</span>.size);
<span style="color: teal;"> 15</span>
<span style="color: teal;"> 16</span> <span style="color: blue;">var</span> dim = <span style="color: blue;">this</span>.size/<span style="color: maroon;">2</span>;
<span style="color: teal;"> 17</span>
<span style="color: teal;"> 18</span> <span style="color: blue;">this</span>.ctx.save();
<span style="color: teal;"> 19</span> <span style="color: blue;">this</span>.ctx.translate(dim, dim);
<span style="color: teal;"> 20</span> <span style="color: blue;">this</span>.drawStar();
<span style="color: teal;"> 21</span> <span style="color: blue;">this</span>.ctx.scale(dim/<span style="color: maroon;">2</span><span style="color: maroon;">.0</span>, dim/<span style="color: maroon;">2</span><span style="color: maroon;">.0</span>);
<span style="color: teal;"> 22</span> <span style="color: blue;">this</span>.ship.draw(<span style="color: blue;">this</span>.ctx);
<span style="color: teal;"> 23</span> <span style="color: blue;">this</span>.ctx.restore();
<span style="color: teal;"> 24</span> }
<span style="color: teal;"> 25</span>
<span style="color: teal;"> 26</span> Simulation.prototype.step = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 27</span> <span style="color: blue;">var</span> ship = <span style="color: blue;">this</span>.ship;
<span style="color: teal;"> 28</span> <span style="color: blue;">var</span> acc = ship.acc;
<span style="color: teal;"> 29</span> <span style="color: blue;">var</span> angle = Math.atan2(ship.pos.x, ship.pos.y);
<span style="color: teal;"> 30</span> <span style="color: blue;">var</span> d2 = ship.pos.x*ship.pos.x + ship.pos.y*ship.pos.y;
<span style="color: teal;"> 31</span> acc.x = -Math.sin(angle) / d2;
<span style="color: teal;"> 32</span> acc.y = -Math.cos(angle) / d2;
<span style="color: teal;"> 33</span> ship.adjust(<span style="color: blue;">this</span>.mult);
<span style="color: teal;"> 34</span> <span style="color: blue;">if</span> (d2 > <span style="color: maroon;">16</span>) {
<span style="color: teal;"> 35</span> <span style="color: blue;">this</span>.stop();
<span style="color: teal;"> 36</span> alert(<span style="color: maroon;">"Ship Left Orbit!"</span>);
<span style="color: teal;"> 37</span> }
<span style="color: teal;"> 38</span> }
<span style="color: teal;"> 39</span>
<span style="color: teal;"> 40</span>
<span style="color: teal;"> 41</span> Simulation.prototype.reset = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 42</span> <span style="color: blue;">this</span>.stop();
<span style="color: teal;"> 43</span> <span style="color: blue;">var</span> ship = <span style="color: blue;">new</span> Ship();
<span style="color: teal;"> 44</span> ship.pos.x = <span style="color: maroon;">1</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 45</span> ship.pos.y = <span style="color: maroon;">0</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 46</span> ship.vel.x = <span style="color: maroon;">0</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 47</span> ship.vel.y = -<span style="color: maroon;">1</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 48</span> <span style="color: blue;">this</span>.ship = ship;
<span style="color: teal;"> 49</span> }
<span style="color: teal;"> 50</span>
<span style="color: teal;"> 51</span> Simulation.prototype.run = <span style="color: blue;">function</span>(mult, refresh) {
<span style="color: teal;"> 52</span> <span style="color: blue;">this</span>.stop();
<span style="color: teal;"> 53</span> <span style="color: blue;">this</span>.mult = mult;
<span style="color: teal;"> 54</span> <span style="color: blue;">var</span> self = <span style="color: blue;">this</span>;
<span style="color: teal;"> 55</span> <span style="color: blue;">this</span>.timer = setInterval(<span style="color: blue;">function</span>() {
<span style="color: teal;"> 56</span> self.step();
<span style="color: teal;"> 57</span> self.draw();
<span style="color: teal;"> 58</span> }, refresh);
<span style="color: teal;"> 59</span> }
<span style="color: teal;"> 60</span>
<span style="color: teal;"> 61</span> Simulation.prototype.stop = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 62</span> clearInterval(<span style="color: blue;">this</span>.timer);
<span style="color: teal;"> 63</span> }</pre>
<span style="font-size: large;">Helper Function</span><br />
Both the above classes call the helper function "circle" for drawing filled in circles.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;">var</span> circle = <span style="color: blue;">function</span>(ctx, x, y, rad) {
<span style="color: teal;"> 2</span> ctx.beginPath();
<span style="color: teal;"> 3</span> ctx.arc(x, y, rad, <span style="color: maroon;">0</span>, <span style="color: maroon;">2</span>*Math.PI, <span style="color: maroon;">false</span>);
<span style="color: teal;"> 4</span> ctx.fill();
<span style="color: teal;"> 5</span> }</pre>
<span style="font-size: large;">Onload</span><br />
We need a few more controls, so the onload portion has a little extra functionality.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> $(<span style="color: blue;">function</span>() {
<span style="color: teal;"> 2</span> <span style="color: blue;">var</span> canvas = document.getElementById(<span style="color: maroon;">"canvas"</span>);
<span style="color: teal;"> 3</span> <span style="color: blue;">var</span> size = <span style="color: maroon;">500</span>;
<span style="color: teal;"> 4</span> canvas.width = size;
<span style="color: teal;"> 5</span> canvas.height = size;
<span style="color: teal;"> 6</span> <span style="color: blue;">var</span> ctx = canvas.getContext(<span style="color: maroon;">'2d'</span>);
<span style="color: teal;"> 7</span> <span style="color: blue;">var</span> sim = <span style="color: blue;">new</span> Simulation(ctx, size);
<span style="color: teal;"> 8</span> sim.draw();
<span style="color: teal;"> 9</span>
<span style="color: teal;"> 10</span> $(<span style="color: maroon;">"#run"</span>).click(<span style="color: blue;">function</span>() {sim.run($(<span style="color: maroon;">"#mult"</span>).val(), $(<span style="color: maroon;">"#refresh"</span>).val())});
<span style="color: teal;"> 11</span> $(<span style="color: maroon;">"#stop"</span>).click(<span style="color: blue;">function</span>() {sim.stop()});
<span style="color: teal;"> 12</span> $(<span style="color: maroon;">"#reset"</span>).click(<span style="color: blue;">function</span>() {sim.reset(); sim.draw();});
<span style="color: teal;"> 13</span> $(<span style="color: maroon;">"#ccw"</span>).click(<span style="color: blue;">function</span>() {sim.ship.turnLeft(); sim.draw();});
<span style="color: teal;"> 14</span> $(<span style="color: maroon;">"#cw"</span>).click(<span style="color: blue;">function</span>() {sim.ship.turnRight(); sim.draw();});
<span style="color: teal;"> 15</span> $(<span style="color: maroon;">"#thruster"</span>).mousedown(<span style="color: blue;">function</span>() {sim.ship.thrusterOn = <span style="color: maroon;">true</span>; sim.draw();});
<span style="color: teal;"> 16</span> $(<span style="color: maroon;">"#thruster"</span>).mouseup(<span style="color: blue;">function</span>() {sim.ship.thrusterOn = <span style="color: maroon;">false</span>; sim.draw();});
<span style="color: teal;"> 17</span> });</pre>
<span style="font-size: x-large;">HTML</span><br />
Again, the HTML is just a canvas along with some controls.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;"><</span><span style="color: maroon;">canvas</span> <span style="color: red;">id</span>="<span style="color: blue;">canvas</span>"<span style="color: blue;">></span><span style="color: blue;"><</span>/<span style="color: maroon;">canvas</span><span style="color: blue;">></span>
<span style="color: teal;"> 2</span> <span style="color: blue;"><</span><span style="color: maroon;">br</span> /<span style="color: blue;">></span>
<span style="color: teal;"> 3</span> Mult: <span style="color: blue;"><</span><span style="color: maroon;">input</span> <span style="color: red;">id</span>="<span style="color: blue;">mult</span>" <span style="color: red;">type</span>="<span style="color: blue;">text</span>" <span style="color: red;">value</span>="<span style="color: blue;">0.01</span>" <span style="color: red;">size</span>="<span style="color: blue;">4</span>" /<span style="color: blue;">></span>
<span style="color: teal;"> 4</span>
<span style="color: teal;"> 5</span> Refresh: <span style="color: blue;"><</span><span style="color: maroon;">input</span> <span style="color: red;">id</span>="<span style="color: blue;">refresh</span>" <span style="color: red;">type</span>="<span style="color: blue;">text</span>" <span style="color: red;">value</span>="<span style="color: blue;">15</span>" <span style="color: red;">size</span>="<span style="color: blue;">3</span>" /<span style="color: blue;">></span>ms
<span style="color: teal;"> 6</span>
<span style="color: teal;"> 7</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">run</span>"<span style="color: blue;">></span>Run<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 8</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">stop</span>"<span style="color: blue;">></span>Stop<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 9</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">reset</span>"<span style="color: blue;">></span>Reset<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 10</span> <span style="color: blue;"><</span><span style="color: maroon;">br</span> /<span style="color: blue;">></span>
<span style="color: teal;"> 11</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">ccw</span>"<span style="color: blue;">></span>CCW<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 12</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">cw</span>"<span style="color: blue;">></span>CW<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 13</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">thruster</span>"<span style="color: blue;">></span>Thruster<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span></pre>Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com1tag:blogger.com,1999:blog-5724099066106449846.post-73616603702624399682012-06-25T09:00:00.000-04:002012-06-25T09:00:06.089-04:00Going in Circles via Html5Today's post is just a simple little HTML5 animation using <a href="http://en.wikipedia.org/wiki/Canvas_element">canvas</a> that moves a spaceship in orbit around a star (not to scale).<br />
<br />
<span style="font-size: x-large;">Demo</span><br />
<div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript">
</script>
<script>
var ns1 = {
namespace : function(name, func) {
var parts = name.split(".");
var space = this;
for (var i in parts) {
var part = parts[i];
var next = space[part];
if (!next) next = {};
next.namespace = this.namespace;
space[part] = next;
space = next;
}
if (func) {
func.call(space);
}
return space;
}
};
ns1.namespace("space", function() {
var Vector = function(x, y) {
if (arguments.length == 0) {
x = 0.0;
y = 0.0;
}
this.x = x;
this.y = y;
};
Vector.prototype.adjust = function(v, mult) {
// if (arguments.length < 3) mult = 1;
this.x += v.x * mult;
this.y += v.y * mult;
}
var Ship = function() {
this.pos = new Vector();
this.vel = new Vector();
this.acc = new Vector();
this.color = "255,255,255";
}
Ship.prototype.adjust = function(time) {
this.vel.adjust(this.acc, time);
this.pos.adjust(this.vel, time);
}
Ship.prototype.draw = function(ctx) {
ctx.save();
ctx.translate(this.pos.x, this.pos.y);
ctx.strokeStyle = "rgb(" + this.color + ")";
ctx.beginPath();
ctx.moveTo(0, -10);
ctx.lineTo(5, 10);
ctx.lineTo(-5, 10);
ctx.lineTo(0, -10);
ctx.stroke();
ctx.restore();
}
var Simulation = function(size) {
this.size = size;
this.reset();
}
Simulation.prototype.drawStar = function(ctx) {
ctx.fillStyle= "#FF0";
ctx.beginPath();
ctx.arc(0, 0, 20, 0, 2*Math.PI, false);
ctx.fill();
}
Simulation.prototype.draw = function(ctx) {
ctx.save();
ctx.fillStyle = "rgba(0,0,0,1.0)";
ctx.fillRect(0, 0, this.size, this.size);
ctx.translate(this.size/2, this.size/2);
this.drawStar(ctx);
this.ship.draw(ctx);
ctx.restore();
}
Simulation.prototype.step = function() {
var ship = this.ship;
var acc = ship.acc;
acc.x = -ship.pos.x;
acc.y = -ship.pos.y;
ship.adjust(this.mult);
}
Simulation.prototype.reset = function() {
this.stop();
var ship = new Ship();
ship.pos.x = this.size / 4;
ship.pos.y = 0;
ship.vel.x = 0;
ship.vel.y = -this.size / 4;
this.ship = ship;
}
Simulation.prototype.run = function(ctx, mult, refresh) {
this.stop();
this.mult = mult;
var self = this;
this.timer = setInterval(function() {
self.step();
self.draw(ctx);
}, refresh);
}
Simulation.prototype.stop = function() {
clearInterval(this.timer);
}
this.Simulation = Simulation;
});
$(function() {
var canvas = document.getElementById("space1_Canvas");
var size = 500;
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
var sim = new ns1.space.Simulation(size);
sim.draw(ctx);
$("#space1_run").click(function() {sim.run(ctx, $("#space1_mult").val(), $("#space1_refresh").val())});
$("#space1_stop").click(function() {sim.stop()});
$("#space1_reset").click(function() {sim.reset(); sim.draw(ctx);});
});
</script>
<canvas id="space1_Canvas"></canvas>
<br />
Mult: <input id="space1_mult" size="4" type="text" value="0.01" />
Refresh: <input id="space1_refresh" size="3" type="text" value="15" />ms
<button id="space1_run">Run</button>
<button id="space1_stop">Stop</button>
<button id="space1_reset">Reset</button>
</div>
<br />
<span style="font-size: x-large;">JavaScript</span><br />
This may be over-engineering for such a simple program, but there are 3 javascript classes used to accomplish this.<br />
<br />
<span style="font-size: large;">Vector</span><br />
(not a <a href="http://docs.oracle.com/javase/6/docs/api/java/util/Vector.html">Java Vector</a> but a <a href="http://en.wikipedia.org/wiki/Euclidean_vector">math vector</a>)<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;">var</span> Vector = <span style="color: blue;">function</span>(x, y) {
<span style="color: teal;"> 2</span> <span style="color: blue;">if</span> (arguments.length == <span style="color: maroon;">0</span>) {
<span style="color: teal;"> 3</span> x = <span style="color: maroon;">0</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 4</span> y = <span style="color: maroon;">0</span><span style="color: maroon;">.0</span>;
<span style="color: teal;"> 5</span> }
<span style="color: teal;"> 6</span> <span style="color: blue;">this</span>.x = x;
<span style="color: teal;"> 7</span> <span style="color: blue;">this</span>.y = y;
<span style="color: teal;"> 8</span> };
<span style="color: teal;"> 9</span>
<span style="color: teal;"> 10</span> Vector.prototype.adjust = <span style="color: blue;">function</span>(v, mult) {
<span style="color: teal;"> 11</span> <span style="color: blue;">this</span>.x += v.x * mult;
<span style="color: teal;"> 12</span> <span style="color: blue;">this</span>.y += v.y * mult;
<span style="color: teal;"> 13</span> }</pre>
<br />
<span style="font-size: large;">Ship</span><br />
A ship consists of 3 vectors (position, velocity, and acceleration), and also knows how to draw itself.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;">var</span> Ship = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 2</span> <span style="color: blue;">this</span>.pos = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 3</span> <span style="color: blue;">this</span>.vel = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 4</span> <span style="color: blue;">this</span>.acc = <span style="color: blue;">new</span> Vector();
<span style="color: teal;"> 5</span> <span style="color: blue;">this</span>.color = <span style="color: maroon;">"255,255,255"</span>;
<span style="color: teal;"> 6</span> }
<span style="color: teal;"> 7</span>
<span style="color: teal;"> 8</span> Ship.prototype.adjust = <span style="color: blue;">function</span>(time) {
<span style="color: teal;"> 9</span> <span style="color: blue;">this</span>.vel.adjust(<span style="color: blue;">this</span>.acc, time);
<span style="color: teal;"> 10</span> <span style="color: blue;">this</span>.pos.adjust(<span style="color: blue;">this</span>.vel, time);
<span style="color: teal;"> 11</span> }
<span style="color: teal;"> 12</span>
<span style="color: teal;"> 13</span> Ship.prototype.draw = <span style="color: blue;">function</span>(ctx) {
<span style="color: teal;"> 14</span> ctx.save();
<span style="color: teal;"> 15</span> ctx.translate(<span style="color: blue;">this</span>.pos.x, <span style="color: blue;">this</span>.pos.y);
<span style="color: teal;"> 16</span> ctx.strokeStyle = <span style="color: maroon;">"rgb("</span> + <span style="color: blue;">this</span>.color + <span style="color: maroon;">")"</span>;
<span style="color: teal;"> 17</span> ctx.beginPath();
<span style="color: teal;"> 18</span> ctx.moveTo(<span style="color: maroon;">0</span>, -<span style="color: maroon;">10</span>);
<span style="color: teal;"> 19</span> ctx.lineTo(<span style="color: maroon;">5</span>, <span style="color: maroon;">10</span>);
<span style="color: teal;"> 20</span> ctx.lineTo(-<span style="color: maroon;">5</span>, <span style="color: maroon;">10</span>);
<span style="color: teal;"> 21</span> ctx.lineTo(<span style="color: maroon;">0</span>, -<span style="color: maroon;">10</span>);
<span style="color: teal;"> 22</span> ctx.stroke();
<span style="color: teal;"> 23</span>
<span style="color: teal;"> 24</span> ctx.restore();
<span style="color: teal;"> 25</span> }</pre>
<br />
<span style="font-size: large;">Simulation</span><br />
A simulation consists of the ship, knows how to accelerate the ship around in an orbit, and has the commands for actually starting, stopping, and running the simulation.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;">var</span> Simulation = <span style="color: blue;">function</span>(size) {
<span style="color: teal;"> 2</span> <span style="color: blue;">this</span>.size = size;
<span style="color: teal;"> 3</span> <span style="color: blue;">this</span>.reset();
<span style="color: teal;"> 4</span> }
<span style="color: teal;"> 5</span>
<span style="color: teal;"> 6</span> Simulation.prototype.drawStar = <span style="color: blue;">function</span>(ctx) {
<span style="color: teal;"> 7</span> ctx.fillStyle= <span style="color: maroon;">"#FF0"</span>;
<span style="color: teal;"> 8</span> ctx.beginPath();
<span style="color: teal;"> 9</span> ctx.arc(<span style="color: maroon;">0</span>, <span style="color: maroon;">0</span>, <span style="color: maroon;">20</span>, <span style="color: maroon;">0</span>, <span style="color: maroon;">2</span>*Math.PI, <span style="color: maroon;">false</span>);
<span style="color: teal;"> 10</span> ctx.fill();
<span style="color: teal;"> 11</span> }
<span style="color: teal;"> 12</span>
<span style="color: teal;"> 13</span> Simulation.prototype.draw = <span style="color: blue;">function</span>(ctx) {
<span style="color: teal;"> 14</span> ctx.save();
<span style="color: teal;"> 15</span> ctx.fillStyle = <span style="color: maroon;">"rgba(0,0,0,1.0)"</span>;
<span style="color: teal;"> 16</span> ctx.fillRect(<span style="color: maroon;">0</span>, <span style="color: maroon;">0</span>, <span style="color: blue;">this</span>.size, <span style="color: blue;">this</span>.size);
<span style="color: teal;"> 17</span> ctx.translate(<span style="color: blue;">this</span>.size/<span style="color: maroon;">2</span>, <span style="color: blue;">this</span>.size/<span style="color: maroon;">2</span>);
<span style="color: teal;"> 18</span> <span style="color: blue;">this</span>.drawStar(ctx);
<span style="color: teal;"> 19</span> <span style="color: blue;">this</span>.ship.draw(ctx);
<span style="color: teal;"> 20</span> ctx.restore();
<span style="color: teal;"> 21</span> }
<span style="color: teal;"> 22</span>
<span style="color: teal;"> 23</span> Simulation.prototype.step = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 24</span> <span style="color: blue;">var</span> ship = <span style="color: blue;">this</span>.ship;
<span style="color: teal;"> 25</span> <span style="color: blue;">var</span> acc = ship.acc;
<span style="color: teal;"> 26</span> acc.x = -ship.pos.x;
<span style="color: teal;"> 27</span> acc.y = -ship.pos.y;
<span style="color: teal;"> 28</span> ship.adjust(<span style="color: blue;">this</span>.mult);
<span style="color: teal;"> 29</span> }
<span style="color: teal;"> 30</span>
<span style="color: teal;"> 31</span>
<span style="color: teal;"> 32</span> Simulation.prototype.reset = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 33</span> <span style="color: blue;">this</span>.stop();
<span style="color: teal;"> 34</span> <span style="color: blue;">var</span> ship = <span style="color: blue;">new</span> Ship();
<span style="color: teal;"> 35</span> ship.pos.x = <span style="color: blue;">this</span>.size / <span style="color: maroon;">4</span>;
<span style="color: teal;"> 36</span> ship.pos.y = <span style="color: maroon;">0</span>;
<span style="color: teal;"> 37</span> ship.vel.x = <span style="color: maroon;">0</span>;
<span style="color: teal;"> 38</span> ship.vel.y = -<span style="color: blue;">this</span>.size / <span style="color: maroon;">4</span>;
<span style="color: teal;"> 39</span> <span style="color: blue;">this</span>.ship = ship;
<span style="color: teal;"> 40</span> }
<span style="color: teal;"> 41</span>
<span style="color: teal;"> 42</span> Simulation.prototype.run = <span style="color: blue;">function</span>(ctx, mult, refresh) {
<span style="color: teal;"> 43</span> <span style="color: blue;">this</span>.stop();
<span style="color: teal;"> 44</span> <span style="color: blue;">this</span>.mult = mult;
<span style="color: teal;"> 45</span> <span style="color: blue;">var</span> self = <span style="color: blue;">this</span>;
<span style="color: teal;"> 46</span> <span style="color: blue;">this</span>.timer = setInterval(<span style="color: blue;">function</span>() {
<span style="color: teal;"> 47</span> self.step();
<span style="color: teal;"> 48</span> self.draw(ctx);
<span style="color: teal;"> 49</span> }, refresh);
<span style="color: teal;"> 50</span> }
<span style="color: teal;"> 51</span>
<span style="color: teal;"> 52</span> Simulation.prototype.stop = <span style="color: blue;">function</span>() {
<span style="color: teal;"> 53</span> clearInterval(<span style="color: blue;">this</span>.timer);
<span style="color: teal;"> 54</span> }</pre>
<br />
<span style="font-size: large;">On Load</span><br />
Then there is the little bit of code to kick everything off, which leverages <a href="http://jquery.com/">jQuery</a>. This just sets the size of the canvas, and links the input controls from the HTML to the <span style="font-family: 'Courier New', Courier, monospace;">Simulation</span> object.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> $(<span style="color: blue;">function</span>() {
<span style="color: teal;"> 2</span> <span style="color: blue;">var</span> canvas = document.getElementById(<span style="color: maroon;">"canvas"</span>);
<span style="color: teal;"> 3</span> <span style="color: blue;">var</span> size = <span style="color: maroon;">500</span>;
<span style="color: teal;"> 4</span> canvas.width = size;
<span style="color: teal;"> 5</span> canvas.height = size;
<span style="color: teal;"> 6</span> <span style="color: blue;">var</span> ctx = canvas.getContext(<span style="color: maroon;">'2d'</span>);
<span style="color: teal;"> 7</span> <span style="color: blue;">var</span> sim = <span style="color: blue;">new</span> Simulation(size);
<span style="color: teal;"> 8</span> sim.draw(ctx);
<span style="color: teal;"> 9</span>
<span style="color: teal;"> 10</span> $(<span style="color: maroon;">"#run"</span>).click(<span style="color: blue;">function</span>() {sim.run(ctx, $(<span style="color: maroon;">"#mult"</span>).val(), $(<span style="color: maroon;">"#refresh"</span>).val())});
<span style="color: teal;"> 11</span> $(<span style="color: maroon;">"#stop"</span>).click(<span style="color: blue;">function</span>() {sim.stop()});
<span style="color: teal;"> 12</span> $(<span style="color: maroon;">"#reset"</span>).click(<span style="color: blue;">function</span>() {sim.reset(); sim.draw(ctx);});
<span style="color: teal;"> 13</span> });</pre>
<br />
<span style="font-size: x-large;">HTML</span><br />
The HTML is just a <a href="http://en.wikipedia.org/wiki/Canvas_element">canvas</a> along with some inputs to control the simulation.<br />
<pre style="text-align: left;"><span style="color: teal;"> 1</span> <span style="color: blue;"><</span><span style="color: maroon;">canvas</span> <span style="color: red;">id</span>="<span style="color: blue;">canvas</span>"<span style="color: blue;">></span><span style="color: blue;"><</span>/<span style="color: maroon;">canvas</span><span style="color: blue;">></span>
<span style="color: teal;"> 2</span> <span style="color: blue;"><</span><span style="color: maroon;">br</span> /<span style="color: blue;">></span>
<span style="color: teal;"> 3</span> Mult: <span style="color: blue;"><</span><span style="color: maroon;">input</span> <span style="color: red;">id</span>="<span style="color: blue;">mult</span>" <span style="color: red;">type</span>="<span style="color: blue;">text</span>" <span style="color: red;">value</span>="<span style="color: blue;">0.01</span>" <span style="color: red;">size</span>="<span style="color: blue;">4</span>" /<span style="color: blue;">></span>
<span style="color: teal;"> 4</span>
<span style="color: teal;"> 5</span> Refresh: <span style="color: blue;"><</span><span style="color: maroon;">input</span> <span style="color: red;">id</span>="<span style="color: blue;">refresh</span>" <span style="color: red;">type</span>="<span style="color: blue;">text</span>" <span style="color: red;">value</span>="<span style="color: blue;">15</span>" <span style="color: red;">size</span>="<span style="color: blue;">3</span>" /<span style="color: blue;">></span>ms
<span style="color: teal;"> 6</span>
<span style="color: teal;"> 7</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">run</span>"<span style="color: blue;">></span>Run<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 8</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">stop</span>"<span style="color: blue;">></span>Stop<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span>
<span style="color: teal;"> 9</span> <span style="color: blue;"><</span><span style="color: maroon;">button</span> <span style="color: red;">id</span>="<span style="color: blue;">reset</span>"<span style="color: blue;">></span>Reset<span style="color: blue;"><</span>/<span style="color: maroon;">button</span><span style="color: blue;">></span></pre>Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-51271762464510375442012-05-07T09:00:00.000-04:002012-05-07T09:00:16.580-04:00Thoughts on InterviewingI've been on both sides of the interviewing table a number of times. Currently I'm on the less stressful side, that of trying to find a good candidate. So how do you find a good candidate?<br />
<br />
<a href="http://www.joelonsoftware.com/">Joel on Software</a> has <a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html">two criteria</a>: Smart, and Gets Things Done. His <a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html">Guerrilla Guide to Interviewing</a> does a good job of explaining what he means, so I won't repeat it. The question is, how do you determine if someone is smart and/or gets things done?<br />
<br />
The Google/Facebook style of interviewing where you have to solve problems on a whiteboard answers the "smart", at least for a certain type of smart, and assuming that you don't freeze up under the pressure of interviews. But what about "gets things done"? The ability to solve problems doesn't mean that you will work diligently. So the best way I know to determine that is to investigate your track record. An internship or trial hire period would be better, but since that isn't typically an option, it instead means trying to figure out from someone's resume and their answers to questions about it. This is very much, an inexact science.<br />
<br />
What about smart? Different positions require different degrees of intelligence. As my boss reminded my, the work that we currently do doesn't require super genius intelligence. However, I maintain that no matter how mundane the job is, it is better to have higher caliber employees doing them. Do you really want to be maintaining the software of someone who is just barely qualified to do the job? Are you really going to go to that person for help when an issue arises? How are you going to learn and improve while working with someone who just wants to get by?<br />
<br />
Therefore, while I am not looking for super-geniuses, I do want someone who is more than just barely competent. So how do I personally test for that?<br />
<br />
Based on Joel's article we now make candidates write some programs on the whiteboard. We are hiring a programming position. If you want the job you should be capable of turning thoughts and ideas into code. I realize that between modern IDEs and the internet, modern day programmers have access to unprecedented levels of help. However, you shouldn't be dependent on that help. If you don't know the difference between AND and OR without looking it up, or you don't know any collection types other than an ArrayList, how can I be confident that you will write quality maintainable code?<br />
<br />
How about theoretical knowledge from a CS program? How important is that? I realize how much importance you put on that will depend on your own skills and experiences. While it may not be important to actually know that the time complexity of a bubble sort in O(n<sup>2</sup>), at some point you will run into code that is running unacceptably slow. If you don't show any knowledge of time complexity, what evidence do I have that you will actually pick out the bottlenecks of a slow program and know how to fix them? (or even better, how to avoid them in the first place?)<br />
<br />
Unfortunately for me, every example above came from recent interviews I've done. Am I wrong to think that a Java developer should be familiar with more than just ArrayList from the java.util Collection classes? Is it random trivia to remember the time complexity of bubble sort? I think these are examples of things people who care about the craft of software development should know, but maybe I am biased by my own experiences?<br />
<br />What do you think?Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-12375007541309021812012-04-16T09:00:00.000-04:002012-04-16T09:00:03.799-04:00Saved By The VMOne of the most exhilarating, but also frustrating, aspects of working in computers is how fast things move. Things that would've been considered disgracefully wasteful years ago, now provide us with solutions that I never would've imagined then. For example the editor that I am using to write this is running inside of my browser. In effect, the browser is acting as a virtual machine, and the editor is just one application running on it. How much CPU "wastage" is there by running in a VM rather than a native application? Who knows, but it doesn't matter, as my laptop isn't even breaking a sweat. And this is just one of dozens of tabs that I have open in the browser, and the browser is just one of over a dozen applications running currently (plus who knows what the OS is doing).<br />
<br />
Not only is CPU time cheap, but so is storage. At work, our network storage takes <a href="http://serverfault.com/questions/70190/how-do-snapshot-backups-work">automatic snapshots</a>, which makes it trivial to retrieve from backups. This would've seemed incredibly wasteful to me not that long ago. This, combined with VMs, saved me a ton of work this past week.<br />
<br />
<a href="http://torquebox.org/">Torquebox</a> 2.0.0 was released recently, and I wanted to get it installed on our development server. While I was doing this, I decided to upgrade the version of Java running on the machine. I downloaded the latest version and installed it. Unfortunately, I wasn't paying attention and I let it install in the default location, rather than our group's standard location. In a moment when I was apparently flashing back to the days of yore, I thought I could just move the Java directory structure to the location where I wanted it. Since the registry settings did not get updated in the process, bad things happened. My first attempt to fix things was to just delete the Java directories, and reinstall it. Unfortunately, Java thought it was still installed, and refused to reinstall. So then I asked Windows to uninstall Java, but a DLL was missing (probably because I had already deleted it), so it couldn't uninstall either.<br />
<br />
What was I to do? I spent some time fiddling with the Windows Registry, but that was getting me nowhere. Finally, I came to my senses and remembered what year it was. Our servers are running on VMs which means it's just an image on a network disk somewhere, which has regular snapshots taken. So I just wandered down the hall and asked one of our system administrators to restore the VM to an image from the day before. A few minutes later, my mess was cleaned up.<br />
<br />
So yes, VMs are wasteful and inefficient, but they sure save time sometimes. And what's all this extra CPU power for, if not to make our lives easier?Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-70689581967406132432012-04-02T09:00:00.000-04:002012-04-02T09:00:07.176-04:00Pair Programming - My ExperiencesI've mentioned pair programming before in the context of the <a href="http://madkingsmusings.blogspot.com/2007/01/psychology-of-extreme-programming.html">psychology of extreme programming</a>, but I haven't said too much about it. This is because I've had very little first hand experience with it. Well, in the last couple of months I've spent a lot of my time pair programming, and here are my preliminary thoughts.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Distractions</span><br />
I have argued that pair programming can cut down on distractions. I find this to be true. When working on my own if I execute a command that I know will take 5 or more seconds, it is very tempting to take this break to check my email or read a <a href="http://slashdot.org/">slashdot</a> article. Unfortunately, while I justify this as being efficient, in reality it turns a 5-10 second pause into a couple minute pause. The change of context also flushes some registers, err.. short term memory, making it harder to be as focused when I first switch back to my development tasks.<br />
<br />
When someone is looking over my shoulder, I am not going to make that context switch.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Motivation</span><br />
I find that my enthusiasm for projects ebbs and flows. I'll be gung-ho for a while, and then my motivation will slip away. Its easier to keep working in a pair, both from the peer-pressure that you don't want to be slacking, and just the fact that its more fun.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Efficiency</span><br />
I've <a href="http://en.wikipedia.org/wiki/Pair_programming#Scientific_studies">always read</a> that pair programming is more productive than one person working alone, but not twice as productive. (measured in terms of how much code gets written per unit time). My experience of the past couple months agrees with this. Of course this ignores other benefits from pair programming, like potentially higher quality code, and the implicit cross-training that happens.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Correctness</span><br />
Pair programming definitely cuts down on mistakes. While the person on the keyboard is directly thinking about what they are doing, the backseat driver can focus on this big picture. These two different views helped us realize shortcomings that would not have been found until production had either of us been working alone. Also, when bugs were encountered, having two people (who think differently) looking at them, meant we were able to resolve them quicker.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Drawbacks</span><br />
There are a few problems that I encountered with pair programming. The most obvious one is scheduling. The two of us paired up have different schedules in our personal lives, so we don't show up at the same time, or leave at the same time, or even take our lunch breaks at the same time. Plus we both have other professional responsibilities that sometimes require one or the other's attention at various times at the day. Thus an eight hour work day results in only four or five hours of useful pairing time.<br />
<br />
The problem that I didn't expect was energy drain. After a couple of weeks of doing primarily pair programming I felt mentally exhausted. While we got a lot done, it wasn't a pace that I can maintain indefinitely. I think the little breaks that I normally build into my day, which were missing when pair programming, helped keep me fresh. I suspect that I can address this problem by intentionally scheduling breaks when pairing up.<br />
<br />
<span class="pullout">it is most effective to work alone for a while</span>The third, and most important, problem is problem solving. There are obviously times when two heads are better than one. But there are also times where the two heads need to work on their own. To solve difficult design problems, or to come up with innovative solutions to a problem, I truly believe it is most effective to work alone for a while. Then, after I've had a chance to think about it in my own way and come to my own conclusions, is the time to meet and discuss the ideas with others. i.e. if we work together on a solution, we will come up with one solution. If we work independently, we will come up with two solutions, from which we can then draw a third solution that is better than what either of us came up with independently, or if we had worked together all along.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">
Conclusion</span><br />
Pair Programming has a place in your toolkit of software development skills. I think it can be really helpful when working on software that you want to be reliable, you want more than one person intimately familiar with the code base, and which doesn't require too much innovative problem solving. However, like most things, moderation is the key. There are definitely times when pair programming will hinder you.<br />
<br />
If you have the opportunity, give it a try. However, be open to the fact that it isn't best for all situations, so don't force it if it doesn't fit.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com3tag:blogger.com,1999:blog-5724099066106449846.post-73199867646870834852011-10-31T09:00:00.000-04:002011-10-31T09:00:20.033-04:00Chasing TechnologyMaybe 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 <a href="http://madkingsmusings.blogspot.com/2011/09/learn-your-libraries.html">learning your libraries</a>, 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 <a href="http://developers.slashdot.org/story/11/10/26/1748201/your-tech-skills-have-a-two-year-half-life">tech skills have a 2 year half life</a>. The comments on that thread seem to mostly disagree with the hypothesis, but there is definitely some truth to it.<br />
<br />
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 <a href="http://stackoverflow.com/">stackoverflow</a>. 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.<br />
<br />
So what's a person supposed to do?<br />
<br />
Well, first, as many commenters from the <a href="http://developers.slashdot.org/story/11/10/26/1748201/your-tech-skills-have-a-two-year-half-life">slashdot article</a> 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.<br />
<br />
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.<br />
<br />
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).<br />
<br />
<span class="pullout">constantly learn</span>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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com3tag:blogger.com,1999:blog-5724099066106449846.post-79420105575848591122011-10-10T09:00:00.000-04:002011-10-10T09:00:03.663-04:00Vexing BugsWhile 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.<br />
<div style="font-family: arial; font-size: small;">
<br /></div>
<span class="Apple-style-span" style="font-size: large;">Intermittent Bugs</span><br />
<div style="font-family: arial; font-size: small;">
It's annoying to perform the exact same set of steps 4 times, have it work 3 of the times, and fail once. When <a href="http://en.wikipedia.org/wiki/Murphy's_law">Murphy</a> 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?</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<span class="Apple-style-span" style="font-size: large;">Library Bugs</span><br />
<div style="font-family: arial; font-size: small;">
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.</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial;">
<span class="Apple-style-span" style="font-size: large;">Production Bugs</span></div>
<div style="font-family: arial; font-size: small;">
While we strive to have our development environment similar to the production environment, there are certain discrepancies 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.</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial;">
<span class="Apple-style-span" style="font-size: x-large;">This Week</span></div>
<div style="font-family: arial; font-size: small;">
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 <a href="http://madkingsmusings.blogspot.com/2011/08/emailing-server-errors.html">server emails about errors</a>. 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 <a href="http://en.wikipedia.org/wiki/WEBrick">WEBrick</a> in development mode, the error <b>never</b> happened.</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial; font-size: small;">
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 <a href="http://stackoverflow.com/questions/189562/what-is-the-proper-name-for-doing-debugging-by-adding-print-statements">adding printfs</a>, I eventually noticed output that looked like:</div>
<blockquote>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<pre><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Starting Web Service Call 1
Starting Web Service Call 2
Exception Caught from Web Service Call 2
Ending Web Service Call 1</span></pre>
</blockquote>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial; font-size: small;">
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. </div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial; font-size: small;">
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. As it turns out, the web service library we were using is not thread safe. However, 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. </div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial; font-size: small;">
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 <a href="http://en.wikipedia.org/wiki/Critical_section">critical section</a>, 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.</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
<div style="font-family: arial;">
<span class="Apple-style-span" style="font-size: x-large;">Moral</span></div>
<div style="font-family: arial; font-size: small;">
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.</div>
<div style="font-family: arial; font-size: small;">
<ul>
<li>If you think its a library bug, you are probably misusing the library. (i.e. <a href="http://madkingsmusings.blogspot.com/2011/09/learn-your-libraries.html">Learn your libraries</a>)</li>
<li>Intermittent errors are indicative of threading issues.</li>
<li>Know the differences between your development and production environments.<br />oh, and maybe most importantly of all</li>
<li>When frustrated, <a href="http://madkingsmusings.blogspot.com/2007/03/dont-get-stuck-in-rut.html">take a break</a>. 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.</li>
</ul>
</div>
<div style="font-family: arial; font-size: small;">
<br /></div>
Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-23182781121689545972011-09-26T09:00:00.000-04:002011-09-26T10:20:53.115-04:00Learn Your Libraries<span class="Apple-style-span" style="font-size: x-large;">Story 1</span><br />
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. 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.<br />
<br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Story 2</span><br />
The other day a friend asked me what I knew about <a href="http://oreilly.com/catalog/javarmi/chapter/ch10.html">Java Serialization</a>. After asking for details, it turns out that he was working on a project where they were doing <a href="http://graphics.stanford.edu/%7Eseander/bithacks.html">bit-diddling</a> 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.<br />
<br />
Upon check the <a href="https://www.google.com/#sclient=psy-ab&hl=en&source=hp&q=serialization+spec&pbx=1&oq=serialization+spec&aq=f&aqi=g2g-v2&aql=&gs_sm=e&gs_upl=51819l54723l11l54933l18l9l10l2l4l0l224l339l0.1.1l2l0&bav=on.2,or.r_gc.r_pw.r_cp.&fp=c6b95d63df18470d&biw=1052&bih=593">Serialization Spec</a>, it turns out their assumption was not valid. 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 <a href="http://madkingsmusings.blogspot.com/2010/08/dont-reuse-rewrite.html">I've mentioned before</a>, I believe that rolling your own is often the way to go.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Story 3</span><br />
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, <a href="http://www.w3schools.com/xpath/xpath_syntax.asp">XPath</a> queries would be made. HTML reports would be created by running giant <a href="http://www.w3schools.com/xsl/xsl_transformation.asp">XSLT</a> 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).<br />
<br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Moral</span><br />
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.<br />
<br />
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. In the third story the benefits of XML were considered without awareness of all of the limitations and restrictions that came along. i.e. We didn't know what we didn't know.<br />
<br />
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 <a href="http://www.passionforbusiness.com/articles/shiny-object-syndrome.htm">shiny objects</a> 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.<br />
<br />
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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com1tag:blogger.com,1999:blog-5724099066106449846.post-63565035440450924522011-09-19T09:00:00.000-04:002011-09-19T09:00:19.074-04:00RSpec Lessons LearnedPreviously I showed some unit tests written in <a href="https://www.relishapp.com/rspec">RSpec</a>. 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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Read the Latest Documentation</span><br />
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 <a href="http://rspec.info/">old documentation</a> 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 <a href="https://www.relishapp.com/rspec">RSpec 2.6</a>. This will definitely be helpful.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Make Tests Self Documenting</span><br />
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 <a href="http://blog.carbonfive.com/2010/10/21/rspec-best-practices/">not everyone agrees</a>, I am a fan of letting the tests document themselves, whenever possible.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Make Use of Context</span><br />
Use the "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">context</span>" keyword to describe what you are doing in a "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">before</span>" block to set up the state necessary for testing.<br />
<div class="p1">
<pre style="background: #000000; color: #d1d1d1;">describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
context <span style="color: #00c4c4;">"empty object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
<span style="color: #9999a9;">#insert tests</span>
<span style="color: #e66170; font-weight: bold;">end</span>
context <span style="color: #00c4c4;">"with inherited object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:base<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
base<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">.</span>prototype <span style="color: #d2cd86;">=</span> base
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #9999a9;">#insert tests</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span></pre>
</div>
<span class="Apple-style-span" style="font-size: x-large;">Make Use of Describe</span><br />
Use the "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">describe</span>" 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 "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">before</span>" block that <b>are</b> the actions being tested, you should you use "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">describe</span>" rather than "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">context</span>"<br />
<div class="p1">
<pre style="background: #000000; color: #d1d1d1;">describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
context <span style="color: #00c4c4;">"empty object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"when assigning via properties"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #9999a9;"># insert checks</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span></pre>
</div>
<span class="Apple-style-span" style="font-size: x-large;">Make Use of Subject</span><br />
If you have multiple tests on a single object, make it the RSpec Subject and put it in a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">describe</span>s block. This way, all of your "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">should</span>" comparisons will be implicitly on this object.<br />
<div class="p1">
<pre style="background: #000000; color: #d1d1d1;">describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
context <span style="color: #00c4c4;">"empty object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should be_empty<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span></pre>
</div>
<span class="Apple-style-span" style="font-size: x-large;">Use Its</span><br />
Often times you want to test the properties of an object. You can use the "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">its</span>" method to have the implicit subject of "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">should</span>" comparisons be the result of the method or array dereference specified.<br />
<div class="p1">
<pre style="background: #000000; color: #d1d1d1;">describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
context <span style="color: #00c4c4;">"empty object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
its<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'newProperty'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
its<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'newMethod'</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span></pre>
</div>
<span class="Apple-style-span" style="font-size: x-large;">Let and Subject are Lazy Loaded</span><br />
The "variables" defined in "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">let</span>" calls and the "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">subject</span>" aren't actually evaluated until they are used. So if you never reference a variable specified in a "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">let</span>", then that code is never executed. This also means that order isn't important. i.e. the following will work:<br />
<pre style="background: #000000; color: #d1d1d1;">let <span style="color: #d2cd86;">(</span>:a<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>b <span style="color: #d2cd86;">+</span> <span style="color: #008c00;">1</span><span style="color: #b060b0;">}</span>
let <span style="color: #d2cd86;">(</span>:b<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span><span style="color: #008c00;">5</span><span style="color: #b060b0;">}</span>
specify<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">"show using let"</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>a<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #d2cd86;">(</span>b<span style="color: #d2cd86;">+</span><span style="color: #008c00;">1</span><span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span></pre>
<span class="Apple-style-span" style="font-size: x-large;">Shared_examples and shared_contexts are Global</span><br />
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 "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">shared_examples_for 'test this object'</span>", 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 "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">test this object</span>".<br />
<br />
There are two different solutions to this problem that I have found. If the shared examples are the same, pull them into a common <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Module</span> 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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Final Thoughts</span><br />
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. 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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Rewritten Tests</span><br />
Using what I have learned, I have rewritten the unit tests <a href="http://madkingsmusings.blogspot.com/2011/09/unit-testing-simple-property-model.html">from before</a>. Here is what they look like now:<br />
<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">require</span> <span style="color: #00c4c4;">'spec_helper'</span>
describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
shared_examples_for <span style="color: #00c4c4;">"simple object"</span> <span style="color: #e66170; font-weight: bold;">do</span> |map, self_keys|
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should have<span style="color: #d2cd86;">(</span>map<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>items<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should include<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">*</span><span style="color: #d2cd86;">(</span>map<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not include<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"self_keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #b060b0;">}</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span> self_keys ||<span style="color: #d2cd86;">=</span> map<span style="color: #d2cd86;">.</span>keys<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should have<span style="color: #d2cd86;">(</span>self_keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>items<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should include<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">*</span>self_keys<span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not include<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"fields"</span> <span style="color: #e66170; font-weight: bold;">do</span>
map<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">each</span> <span style="color: #e66170; font-weight: bold;">do</span> |k, v|
its<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">[</span>k<span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> v<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
its<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'noSuchProperty'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"methods"</span> <span style="color: #e66170; font-weight: bold;">do</span>
map<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">each</span> <span style="color: #e66170; font-weight: bold;">do</span> |k, v|
its<span style="color: #d2cd86;">(</span>k<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> v<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
its<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'noSuchMethod'</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"to_hash"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should have<span style="color: #d2cd86;">(</span>map<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>items<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should include<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">*</span>map<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should include<span style="color: #d2cd86;">(</span>map<span style="color: #d2cd86;">)</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
context <span style="color: #00c4c4;">"empty object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should be_empty<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
its<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'newProperty'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
its<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'newMethod'</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>should be_nil<span style="color: #b060b0;">}</span>
describe <span style="color: #00c4c4;">"to_hash"</span> <span style="color: #e66170; font-weight: bold;">do</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>should have<span style="color: #d2cd86;">(</span><span style="color: #008c00;">0</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>items<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"when assigning via properties"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple object"</span>, <span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"when assigning via methods"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">.</span>foo <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">.</span>bar <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple object"</span>, <span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
context <span style="color: #00c4c4;">"with inherited object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:base<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
base<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
base<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
thing<span style="color: #d2cd86;">.</span>prototype <span style="color: #d2cd86;">=</span> base
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bye"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple object"</span>,
<span style="color: #b060b0;">{</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span>, <span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">15</span>, <span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>, <span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'baz'</span>, <span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">]</span>
describe <span style="color: #00c4c4;">"when overriding values"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">25</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hola"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple object"</span>, <span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">25</span>, <span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hola"</span>, <span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">15</span>, <span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"bye"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>
<br />
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.<br />
<br />
<pre style="background: #000000; color: #d1d1d1;">$ rspec spec<span style="color: #d2cd86;">/</span>models<span style="color: #d2cd86;">/</span>thing_spec<span style="color: #d2cd86;">.</span>rb
Thing
empty object
keys
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should be empty
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"newProperty"</span><span style="color: #d2cd86;">]</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
newMethod
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should have <span style="color: #008c00;">0</span> items
<span style="color: #e66170; font-weight: bold;">when</span> assigning via properties
it should behave like simple object
keys
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
self_keys
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
fields
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">]</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
methods
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
noSuchMethod
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should include <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">when</span> assigning via methods
it should behave like simple object
keys
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
self_keys
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
fields
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">]</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
methods
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
noSuchMethod
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bar"</span>
should include <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
with inherited object
it should behave like simple object
keys
should have <span style="color: #008c00;">4</span> items
should include <span style="color: #00c4c4;">"foo"</span>, <span style="color: #00c4c4;">"bar"</span>, <span style="color: #00c4c4;">"baz"</span>, <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
self_keys
should have <span style="color: #008c00;">2</span> items
should include <span style="color: #00c4c4;">"baz"</span> <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
fields
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bye"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">]</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
methods
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
baz
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
bye
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
noSuchMethod
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should have <span style="color: #008c00;">4</span> items
should include <span style="color: #00c4c4;">"foo"</span>, <span style="color: #00c4c4;">"bar"</span>, <span style="color: #00c4c4;">"baz"</span>, <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should include <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">5</span>, <span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hello"</span>, <span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">15</span>, <span style="color: #00c4c4;">"bye"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">when</span> overriding values
it should behave like simple object
keys
should have <span style="color: #008c00;">4</span> items
should include <span style="color: #00c4c4;">"foo"</span>, <span style="color: #00c4c4;">"bar"</span>, <span style="color: #00c4c4;">"baz"</span>, <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
self_keys
should have <span style="color: #008c00;">4</span> items
should include <span style="color: #00c4c4;">"foo"</span>, <span style="color: #00c4c4;">"bar"</span>, <span style="color: #00c4c4;">"baz"</span>, <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #e66170; font-weight: bold;">not</span> include <span style="color: #00c4c4;">"noSuchProperty"</span>
fields
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">25</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hola"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"bye"</span><span style="color: #d2cd86;">]</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">"noSuchProperty"</span><span style="color: #d2cd86;">]</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
methods
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">25</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hola"</span>
baz
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
bye
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
noSuchMethod
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should have <span style="color: #008c00;">4</span> items
should include <span style="color: #00c4c4;">"foo"</span>, <span style="color: #00c4c4;">"bar"</span>, <span style="color: #00c4c4;">"baz"</span>, <span style="color: #e66170; font-weight: bold;">and</span> <span style="color: #00c4c4;">"bye"</span>
should include <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"foo"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">25</span>, <span style="color: #00c4c4;">"bar"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"hola"</span>, <span style="color: #00c4c4;">"baz"</span><span style="color: #d2cd86;">=</span>><span style="color: #008c00;">15</span>, <span style="color: #00c4c4;">"bye"</span><span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>
Finished <span style="color: #e66170; font-weight: bold;">in</span> <span style="color: #009f00;">0.0831</span> seconds
<span style="color: #008c00;">78</span> examples, <span style="color: #008c00;">0</span> failures
</pre>
Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-19786431422756192532011-09-12T09:00:00.001-04:002011-09-12T09:00:08.553-04:00Birth of a BugThis 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.<br />
<br />
<span class="pullout">let people configure which emails they get</span>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.<br />
<br />
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.<br />
<br />
Why didn't it work? How did such a fundamental bug get into the application?<br />
<br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">NotificationPreferences</span> object is a line that looks similar to:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> <span style="color: #d2cd86;">(</span>amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mMoney<span style="color: #d2cd86;">)</span><span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre><div>where <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">amount</span> is the amount the form is for ($53.24 stapler), and <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mMoney</span> is the threshold the user set ($10,000). There's a catch though - what is <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mMoney</span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">NULL</span>, but if they have set a preference for configuration, but never set a threshold, this value gets set to <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">0</span>. This means that we do NOT want to send a financial override if the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mMoney</span> value is either <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">NULL</span> or <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">0</span>. The actual code looked like:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> mMoney <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> mMoney <span style="color: #d2cd86;">></span> <span style="color: #008c00;">0</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mMoney<span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre>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. While the bug was simple, there were a number of "best practices" that were violated that led to the birth of this bug.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Don't Make Last Minute Changes</span><br />
The plan was to deploy the code at 5PM. At 3PM, I had a coworker who suggested that "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">amount</span>" was a better name for the database column for the threshold than "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">money</span>" because "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">amount</span>" is consistent with other projects. (and, of course, "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mAmount</span>" would be the Java variable containing the threshold value.)<br />
<div><br />
</div>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.<br />
<br />
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. I never should've agreed to make this change.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Beware of Semantic Code Changes</span><br />
When making this last minute name change from "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">money</span>" to "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">amount</span>" 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:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> mAmount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mAmount<span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre>As you can see, the condition for checking if "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mAmount > 0</span>" 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.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Understand Your Changes</span><br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Leverage Refactoring Tools</span><br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Check Your Changes Before Committing</span><br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Run Your Unit Tests</span><br />
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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Write Understandable Code</span><br />
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:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> mMoney <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> mMoney <span style="color: #d2cd86;">></span> <span style="color: #008c00;">0</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mMoney<span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre>Checking for null is fairly obvious as it prevents <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">NullPointerExceptions</span> when the <a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/autoboxing.html">autounboxing</a> happens. The comparison "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">amount >= mMoney</span>" is the comparison that is needed. But what's with the "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mMoney > 0</span>"? 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:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #9999a9;">// if mMoney <= 0, it is not a valid value and should be ignored</span>
<span style="color: #e66170; font-weight: bold;">return</span> mMoney <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> mMoney <span style="color: #d2cd86;">></span> <span style="color: #008c00;">0</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mMoney<span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre>But since I think comments are often a sign of confusing code, my other suggestion involves changing the code:<br />
<pre style="background: #000000; color: #d1d1d1;">boolean checkFinancialOverride(Double amount) <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> isThresholdValid<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">)</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> amount <span style="color: #d2cd86;">></span><span style="color: #d2cd86;">=</span> mMoney<span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
boolean isThresholdValid() <span style="color: #b060b0;">{</span>
<span style="color: #e66170; font-weight: bold;">return</span> mMoney <span style="color: #d2cd86;">!</span><span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">null</span> <span style="color: #d2cd86;">&</span><span style="color: #d2cd86;">&</span> mMoney <span style="color: #d2cd86;">></span> <span style="color: #008c00;">0</span>
<span style="color: #b060b0;">}</span>
</pre><br />
<span class="Apple-style-span" style="font-size: x-large;">Conclusion</span><br />
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.<br />
<div></div></div>Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-3805372707181088652011-09-05T09:00:00.009-04:002011-09-05T09:00:11.125-04:00Unit Testing a Simple Property ModelPreviously I showed the beginning of a <a href="http://madkingsmusings.blogspot.com/2011/08/simple-property-model-in-ruby.html">Ruby implementation of a simple property model</a>. Well, I decided to create some unit tests to help verify that my property model correctly handles properties, especially with the prototype delegation.<br />
<br />
<a href="http://rspec.info/">RSpec</a> seems to be popular in the rails world, so I used rspec to write my tests. This exercise provided me with examples of the <a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-good.html">good</a>, the <a href="http://madkingsmusings.blogspot.com/2011/01/unit-testing-bad.html">bad</a>, and the <a href="http://madkingsmusings.blogspot.com/2011/02/unit-testing-ugly.html">ugly</a> 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.<br />
<br />
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 "<a href="http://rspec.info/documentation/">shared_examples_for</a>" 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 <a href="http://madkingsmusings.blogspot.com/2011/02/unit-testing-ugly.html?showComment=1300759434884#c5639012959821131617">Trevor commented</a>, 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.<br />
<br />
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.<br />
<br />
And without further ado, here are these unit tests.<br />
<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">require</span> <span style="color: #00c4c4;">'spec_helper'</span>
describe Thing <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:foo<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span><span style="color: #008c00;">5</span><span style="color: #b060b0;">}</span>
let<span style="color: #d2cd86;">(</span>:bar<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
shared_examples_for <span style="color: #00c4c4;">"simple properties"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> foo<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>foo<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> foo<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"bar"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> bar<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>bar<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> bar<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"baz"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>baz<span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"to_hash"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> foo<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> bar<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
shared_examples_for <span style="color: #00c4c4;">"nested properties"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'b2'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"foo"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>foo<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"bar"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>bar<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"b2"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'b2'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>b2<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"bye"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>bye<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"baz"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>baz<span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"to_hash"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'b2'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"no inheritance"</span> <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #b060b0;">}</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
describe <span style="color: #00c4c4;">"new object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
describe <span style="color: #00c4c4;">"keys"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">.</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>keys<span style="color: #d2cd86;">.</span>should be_empty<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"properties"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>foo<span style="color: #d2cd86;">.</span>should be_nil<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"to_hash"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>should_not be_nil<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>to_hash<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">0</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"adding properties to object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple properties"</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"adding fields to object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">.</span>foo <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
thing<span style="color: #d2cd86;">.</span>bar <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple properties"</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"inheritance"</span> <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:thing<span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
base <span style="color: #d2cd86;">=</span> Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span>
base<span style="color: #d2cd86;">.</span>foo <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
base<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">'hello'</span>
foo <span style="color: #d2cd86;">=</span> Thing<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span>
foo<span style="color: #d2cd86;">.</span>prototype <span style="color: #d2cd86;">=</span> base
foo
<span style="color: #e66170; font-weight: bold;">end</span>
subject <span style="color: #b060b0;">{</span>thing<span style="color: #b060b0;">}</span>
describe <span style="color: #00c4c4;">"new object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
it_should_behave_like <span style="color: #00c4c4;">"simple properties"</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"adding properties to object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'b2'</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
thing<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'bye'</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"nested properties"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"adding fields to object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">.</span>b2 <span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
thing<span style="color: #d2cd86;">.</span>bye <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"nested properties"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
describe <span style="color: #00c4c4;">"adding redundant fields to object"</span> <span style="color: #e66170; font-weight: bold;">do</span>
let<span style="color: #d2cd86;">(</span>:foo<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span><span style="color: #008c00;">15</span><span style="color: #b060b0;">}</span>
let<span style="color: #d2cd86;">(</span>:bar<span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span><span style="color: #00c4c4;">"bar2"</span><span style="color: #b060b0;">}</span>
before<span style="color: #d2cd86;">(</span>:<span style="color: #e66170; font-weight: bold;">each</span><span style="color: #d2cd86;">)</span> <span style="color: #e66170; font-weight: bold;">do</span>
thing<span style="color: #d2cd86;">.</span>foo <span style="color: #d2cd86;">=</span> foo
thing<span style="color: #d2cd86;">.</span>bar <span style="color: #d2cd86;">=</span> bar
<span style="color: #e66170; font-weight: bold;">end</span>
it_should_behave_like <span style="color: #00c4c4;">"simple properties"</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>size<span style="color: #d2cd86;">.</span>should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span><span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'foo'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'bar'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_true<span style="color: #b060b0;">}</span>
it <span style="color: #b060b0;">{</span>thing<span style="color: #d2cd86;">.</span>self_keys<span style="color: #d2cd86;">.</span>include?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'baz'</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>should be_false<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre><br />
<br />
And here is the output of running the above tests:<br />
<br />
<br />
<pre style="background: #000000; color: #d1d1d1;">$ rspec spec<span style="color: #d2cd86;">/</span>models<span style="color: #d2cd86;">/</span>thing_spec<span style="color: #d2cd86;">.</span>rb
Thing
no inheritance
<span style="color: #e66170; font-weight: bold;">new</span> object
keys
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should be empty
properties
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">0</span>
adding properties to object
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
it should behave like simple properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
adding fields to object
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
it should behave like simple properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
inheritance
<span style="color: #e66170; font-weight: bold;">new</span> object
should be <span style="color: #e66170; font-weight: bold;">false</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
it should behave like simple properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
adding properties to object
it should behave like nested properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
b2
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
bye
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
adding fields to object
it should behave like nested properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
b2
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
bye
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">4</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">5</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"hello"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bye"</span>
adding redundant fields to object
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">true</span>
should be <span style="color: #e66170; font-weight: bold;">false</span>
it should behave like simple properties
keys
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
foo
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
bar
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bar2"</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bar2"</span>
baz
should be <span style="color: #e66170; font-weight: bold;">nil</span>
should be <span style="color: #e66170; font-weight: bold;">nil</span>
to_hash
should <span style="color: #e66170; font-weight: bold;">not</span> be <span style="color: #e66170; font-weight: bold;">nil</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">2</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #008c00;">15</span>
should <span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"bar2"</span>
Finished <span style="color: #e66170; font-weight: bold;">in</span> <span style="color: #009f00;">0.06217</span> seconds
<span style="color: #008c00;">106</span> examples, <span style="color: #008c00;">0</span> failures
</pre><br />
<br />
Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com1tag:blogger.com,1999:blog-5724099066106449846.post-24090663032983169532011-08-29T09:00:00.003-04:002011-08-29T09:00:08.459-04:00Simple Property Model in RubyI've <a href="http://madkingsmusings.blogspot.com/2010/08/learning-javascript.html">talked before</a> about how JavaScript follows what Steve Yegge calls the <a href="http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html">Universal Design Pattern</a>. 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.<br />
<div><div><br />
</div><div><span class="Apple-style-span" style="font-size: x-large;">Code</span></div><div>First I'll show you the class that stores a property. It is a just a simple key/value pair.</div><pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">class</span> Property
attr_accessor :name, :value
<span style="color: #e66170; font-weight: bold;">def</span> initialize<span style="color: #d2cd86;">(</span>name, value<span style="color: #d2cd86;">)</span>
@name <span style="color: #d2cd86;">=</span> name
@value <span style="color: #d2cd86;">=</span> value
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre><div>Next, is my code for the Object class (which I call <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Thing</span>):</div></div><pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">class</span> Thing
attr_accessor :prototype, :type, :properties
<span style="color: #e66170; font-weight: bold;">def</span> initialize
@properties <span style="color: #d2cd86;">=</span> <span style="color: #b060b0;">{</span><span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> self_keys
@properties<span style="color: #d2cd86;">.</span>keys
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> keys
self_keys | <span style="color: #d2cd86;">(</span>@prototype ? @prototype<span style="color: #d2cd86;">.</span>keys : <span style="color: #d2cd86;">[</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> []<span style="color: #d2cd86;">(</span>key<span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;">return</span> @properties<span style="color: #d2cd86;">[</span>key<span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>value <span style="color: #e66170; font-weight: bold;">if</span> @properties<span style="color: #d2cd86;">.</span>has_key? key
@prototype ? @prototype<span style="color: #d2cd86;">[</span>key<span style="color: #d2cd86;">]</span> : <span style="color: #e66170; font-weight: bold;">nil</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> []<span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">(</span>key, value<span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;"> if</span> @properties<span style="color: #d2cd86;">.</span>has_key? key
@properties<span style="color: #d2cd86;">[</span>key<span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">.</span>value <span style="color: #d2cd86;">=</span> value
<span style="color: #e66170; font-weight: bold;">else</span>
@properties<span style="color: #d2cd86;">[</span>key<span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> Property<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #d2cd86;">(</span>key, value<span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> to_hash
hash <span style="color: #d2cd86;">=</span> @prototype ? @prototype<span style="color: #d2cd86;">.</span>to_hash : <span style="color: #b060b0;">{</span><span style="color: #b060b0;">}</span>
@properties<span style="color: #d2cd86;">.</span>each_value <span style="color: #b060b0;">{</span>|<span style="color: #e66170; font-weight: bold;">p</span>| hash<span style="color: #d2cd86;">[</span><span style="color: #e66170; font-weight: bold;">p</span><span style="color: #d2cd86;">.</span>name<span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">p</span><span style="color: #d2cd86;">.</span>value<span style="color: #b060b0;">}</span>
hash
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> method_missing<span style="color: #d2cd86;">(</span>method, <span style="color: #d2cd86;">*</span>args<span style="color: #d2cd86;">)</span>
name <span style="color: #d2cd86;">=</span> method<span style="color: #d2cd86;">.</span>to_s
<span style="color: #e66170; font-weight: bold;"> if</span> <span style="color: #d2cd86;">(</span>name<span style="color: #d2cd86;">.</span>end_with?<span style="color: #d2cd86;">(</span><span style="color: #00c4c4;">'='</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;">self</span><span style="color: #d2cd86;">[</span>name<span style="color: #d2cd86;">[</span><span style="color: #008c00;">0</span><span style="color: #d2cd86;">.</span><span style="color: #d2cd86;">.</span><span style="color: #d2cd86;">-</span><span style="color: #008c00;">2</span><span style="color: #d2cd86;">]</span><span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> args<span style="color: #d2cd86;">[</span><span style="color: #008c00;">0</span><span style="color: #d2cd86;">]</span>
<span style="color: #e66170; font-weight: bold;">else</span>
<span style="color: #e66170; font-weight: bold;">return</span> <span style="color: #e66170; font-weight: bold;">self</span><span style="color: #d2cd86;">[</span>name<span style="color: #d2cd86;">]</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>As you can see a "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Thing</span>" consists of a type, a prototype object, and a map of key/property pairs. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Property</span> 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.<br />
<div><br />
</div><div><span class="Apple-style-span" style="font-size: x-large;">Thoughts</span></div><div>There are a couple of decisions here that seem odd, so I'll explain my thinking.</div><div><br />
</div><div><span class="Apple-style-span" style="font-size: large;">Thing</span></div><div>Why did I call the Object type <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Thing</span>? Well there is already an <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Object</span> 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.</div><div><br />
<span class="Apple-style-span" style="font-size: large;">Property</span><br />
Why do I have a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Property</span> class rather than doing the more straightforward thing of having <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Thing</span> just containing a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Hash</span> of key/values? Well, this is an example of me violating the <a href="http://en.wikipedia.org/wiki/You_ain't_gonna_need_it">YAGNI principle</a>. I am going to want to persist this information to a database so I am envisioning a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Thing</span> table and a <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Property</span> table. My assumption is that when I do that, I'll want the key/value pairs broken out into their own <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Property</span> class. So I am (perhaps foolishly) breaking that out ahead of time.</div><div><br />
<span class="Apple-style-span" style="font-size: large;">method_missing</span><br />
Like JavaScript, I want to be able access properties via either array syntax (<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">thing['property']</span>) or method syntax (<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">thing.property</span>). Ruby provides the functionality to <a href="http://rubylearning.com/satishtalim/ruby_method_missing.html">handle unknown method names</a>, via the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">method_missing</span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">respond_to</span> so it will <a href="http://www.dan-manges.com/blog/30">return true for any property that exists</a>.<br />
<br />
</div><div><span class="Apple-style-span" style="font-size: large;">Lack of Functionality</span></div><div>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.</div>Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-55873250589759813472011-08-22T09:00:00.006-04:002011-08-22T09:00:12.301-04:00Wrong is Better Than Vague<span class="Apple-style-span" style="font-size: x-large;">Vague</span><br />
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?<br />
<br />
<blockquote>What's the game? <i>Oh, something fun.</i><br />
So this will be for the iPad? <i>That and other tablets.</i><br />
Who's going to play it? <i>Everyone.</i></blockquote><br />
<span class="Apple-style-span" style="font-size: x-large;">Wrong</span><br />
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?<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">The Difference</span><br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<blockquote>Nobody pays $400 for apps! <i>Oh, then I guess we can charge $4.95.</i> </blockquote><blockquote>The iPad doesn't support 3D. <i>Hmm.. then maybe we should target those <a href="http://3d-mobile-phones.org/">cell phones that do</a>.</i> </blockquote><blockquote>Minesweeper really doesn't lend itself to 3D. <i>Well, what about this other [fill in specific details here] game?</i> </blockquote><blockquote>How much work will this be? <i>Oh, given those details it'll take about 3 months.</i> </blockquote><blockquote>So how many do have to sell to make it worthwhile? <i>Oh, my time is cheap, probably just 10 copies.</i> </blockquote><blockquote>Then you'll probably be successful, but I don't think I'll help you.</blockquote>As you can see, with concreteness, even wrong concreteness, decisions can be made in an informed way.<br />
<br />
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.<br />
<br />
So the next time you have an idea, make it concrete. A wrong implementation is worth much more than just the vague idea itself.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com3tag:blogger.com,1999:blog-5724099066106449846.post-8313529770638503832011-08-15T09:00:00.001-04:002011-08-15T09:00:09.464-04:00Emailing Server ErrorsI've talked about <a href="http://madkingsmusings.blogspot.com/2011/07/checking-web-application-health.html">checking the server's health</a>, 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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Catch the Error</span><br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ApplicationController</span>.<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">unless</span> Rails<span style="color: #d2cd86;">.</span>application<span style="color: #d2cd86;">.</span>config<span style="color: #d2cd86;">.</span>consider_all_requests_local <span style="color: #9999a9;"># don't rescue on local testing</span>
rescue_from Exception, :with <span style="color: #d2cd86;">=</span>> :handle_error
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>As of this writing, apparently Rail 3 <a href="http://techoctave.com/c7/posts/36-rails-3-0-rescue-from-routing-error-solution">doesn't support 404 errors this way</a>, so a fix for this is to add a catch-all route at the bottom of your <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">config/routes.rb</span> that looks something like:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #9999a9;"># this must be the LAST rule in the file</span>
<span style="color: #9999a9;"># it allows our custom 404 error to execute</span>
match <span style="color: #00c4c4;">'*path'</span>, :to <span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">'application#routing'</span>
</pre><span class="Apple-style-span" style="font-size: x-large;">Handle the Error</span><br />
Now that I have caught unhandled exceptions and 404 errors, you need to do something with them. These methods in the the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ApplicationController</span> are the ones called from the above code:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">def</span> routing<span style="color: #d2cd86;">(</span>exception <span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">nil</span><span style="color: #d2cd86;">)</span>
record_error <span style="color: #00c4c4;">"404 error"</span>, exception
render :template <span style="color: #d2cd86;">=</span>> <span style="color: #00c4c4;">"/errors/404.html"</span>, :status <span style="color: #d2cd86;">=</span>> <span style="color: #008c00;">404</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> handle_error<span style="color: #d2cd86;">(</span>exception<span style="color: #d2cd86;">)</span>
msg <span style="color: #d2cd86;">=</span> exception ? exception<span style="color: #d2cd86;">.</span>message : <span style="color: #00c4c4;">"unknown error"</span>
record_error<span style="color: #d2cd86;">(</span>msg, exception<span style="color: #d2cd86;">)</span>
render :template <span style="color: #d2cd86;">=</span>> <span style="color: #00c4c4;">"/errors/500.html"</span>, :status <span style="color: #d2cd86;">=</span>> <span style="color: #008c00;">500</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">def</span> record_error<span style="color: #d2cd86;">(</span>msg, exception<span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;">return</span> <span style="color: #e66170; font-weight: bold;">nil</span> <span style="color: #e66170; font-weight: bold;">if</span> Rails<span style="color: #d2cd86;">.</span>application<span style="color: #d2cd86;">.</span>config<span style="color: #d2cd86;">.</span>consider_all_requests_local <span style="color: #9999a9;"># don't email during local testing</span>
referrer <span style="color: #d2cd86;">=</span> request<span style="color: #d2cd86;">.</span>env<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'HTTP_REFERER'</span><span style="color: #d2cd86;">]</span>
url <span style="color: #d2cd86;">=</span> request<span style="color: #d2cd86;">.</span>url
app_name <span style="color: #d2cd86;">=</span> Rails<span style="color: #d2cd86;">.</span>application<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">class</span><span style="color: #d2cd86;">.</span>to_s
user <span style="color: #d2cd86;">=</span> current_user
name <span style="color: #d2cd86;">=</span> user ? user<span style="color: #d2cd86;">.</span>username : <span style="color: #00c4c4;">"UNKNOWN"</span>
error_msg <span style="color: #d2cd86;">=</span> msg
<span style="color: #e66170; font-weight: bold;"> if</span> exception
error_msg <span style="color: #d2cd86;">+</span><span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">" #{exception.message}\n#{exception.backtrace.join '\n'}"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
ip <span style="color: #d2cd86;">=</span> request<span style="color: #d2cd86;">.</span>remote_ip
remote_ip <span style="color: #d2cd86;">=</span> request<span style="color: #d2cd86;">.</span>env<span style="color: #d2cd86;">[</span><span style="color: #00c4c4;">'HTTP_X_FORWARDED_FOR'</span><span style="color: #d2cd86;">]</span>
logger<span style="color: #d2cd86;">.</span>warn <span style="color: #00c4c4;">"Error #{error_msg} for url [#{url}] coming from url [#{referrer}] from user [#{user}] at ip [#{ip}] forwarded from ip [#{remote_ip}]"</span>
mail <span style="color: #d2cd86;">=</span> ErrorMailer<span style="color: #d2cd86;">.</span>log_error<span style="color: #d2cd86;">(</span>msg, app_name, url, referrer, ip, remote_ip, name, exception<span style="color: #d2cd86;">)</span>
mail<span style="color: #d2cd86;">.</span>deliver
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>You can make the 404 and 500 error pages that these methods render be whatever you like, though it is suggested that you <a href="http://www.codinghorror.com/blog/2007/03/creating-user-friendly-404-pages.html">make them useful</a>. 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 <a href="http://guides.rubyonrails.org/action_mailer_basics.html">Rails mailing capability</a>. Since the actual configuration for talking to the mail server will depend on your situation, you should read the <a href="http://guides.rubyonrails.org/action_mailer_basics.html">documentation</a> for that.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Emailer</span><br />
To send the email, I created <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">mailers/error_mailer.rb</span> which is analogous to a controller. All it does is package all the variables into instance variables that the view will have:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">class</span> ErrorMailer < ActionMailer::Base
default :from <span style="color: #d2cd86;">=</span>> <span style="color: #00c4c4;">"myApplication@myurl.com"</span>
helper :application
<span style="color: #e66170; font-weight: bold;">def</span> log_error<span style="color: #d2cd86;">(</span>subject, app_name, url, referrer, ip, remote_ip, user_name, error<span style="color: #d2cd86;">)</span>
@app_name <span style="color: #d2cd86;">=</span> app_name
@url <span style="color: #d2cd86;">=</span> url
@referrer <span style="color: #d2cd86;">=</span> referrer
@ip <span style="color: #d2cd86;">=</span> ip
@remote_ip <span style="color: #d2cd86;">=</span> remote_ip
@user_error <span style="color: #d2cd86;">=</span> user_error
@user_name <span style="color: #d2cd86;">=</span> user_name
@error <span style="color: #d2cd86;">=</span> error
mail :to <span style="color: #d2cd86;">=</span>> <span style="color: #00c4c4;">"myAddress@somewhere.com"</span>, :subject<span style="color: #d2cd86;">=</span>><span style="color: #00c4c4;">"RAILS ERROR in #{@app_name}: #{subject}"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>The view (<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">views/error_mailer/log_error.text.haml</span>) turns this information into an email message:<br />
<pre style="background: #000000; color: #d1d1d1;">There was an error <span style="color: #e66170; font-weight: bold;">in</span> app <span style="color: #9999a9;">#{@app_name} at time #{Time.now}.</span>
URL: <span style="color: #9999a9;">#{@url}</span>
Referrer: <span style="color: #9999a9;">#{@referrer}</span>
IP: <span style="color: #9999a9;">#{@ip}</span>
IP via a portal: <span style="color: #9999a9;">#{@remote_ip}</span>
User: <span style="color: #9999a9;">#{@user_name}</span>
<span style="color: #d2cd86;">-</span> <span style="color: #e66170; font-weight: bold;">if</span> @error
Error <span style="color: #e66170; font-weight: bold;">class</span>: <span style="color: #9999a9;">#{@error.class}</span>
Error message: <span style="color: #9999a9;">#{@error.message}</span>
Error Stack Trace:
<span style="color: #d2cd86;">-</span> @error<span style="color: #d2cd86;">.</span>backtrace<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">each</span> <span style="color: #e66170; font-weight: bold;">do</span> |line|
<span style="color: #d2cd86;">=</span> line
<span style="color: #d2cd86;">-</span> <span style="color: #e66170; font-weight: bold;">else</span>
No exception to log
</pre><span class="Apple-style-span" style="font-size: x-large;">Warning</span><br />
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.<br />
<br />
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).<br />
<br />
Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com4tag:blogger.com,1999:blog-5724099066106449846.post-90832260802179840632011-08-08T09:00:00.003-04:002011-08-08T09:00:19.523-04:00One year of regular postsThough 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 <a href="http://www.codinghorror.com/blog/2007/10/how-to-achieve-ultimate-blog-success-in-one-easy-step.html">post regularly</a>. 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 a popular blogger than just being reliable. However, being reliable is definitely a prerequisite.<br />
<br />
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, <a href="http://www.google.com/analytics/">Google Analytics</a> shows that I've been at least a little bit successful. I've trended up pretty consistently since I started posting regularly.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><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;"><img border="0" height="195" src="http://2.bp.blogspot.com/-fRLnNVINcMI/Tj85OUbwxWI/AAAAAAAABBc/cc_0WUvc62M/s640/usage-2011-08.png" width="640" /></a></div><br />
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.<br />
<br />
If you've been following my blog you've noticed that I have two different types of posts, my philosophical 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.<br />
<br />
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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com4tag:blogger.com,1999:blog-5724099066106449846.post-42902537728272361682011-08-01T09:00:00.001-04:002011-08-01T09:00:00.394-04:00Documentation and ContextA few weeks ago Scott Adams (of Dilbert fame) wrote a <a href="http://www.dilbert.com/blog/entry/author_by_relocation/">blog post</a> 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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<span class="pullout">How should [we] document our designs</span>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 behemoths 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?<br />
<br />
Some people say that the <a href="http://www.commonsense4commonpeople.net/2008/11/the-code-is-the-documentation.html">code is the documentation</a>. 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.<br />
<br />
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?Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-40255317006477598132011-07-25T09:00:00.001-04:002011-07-25T09:00:12.079-04:00Checking Web Application HealthThere are certain things you want to know about your web application to make sure it has started correctly and to help diagnose problems.<br />
<ul><li>Is it up?</li>
<li>Can it connect to the database(s)?</li>
<li>Can it connect to the web service(s)?</li>
<li>Can it do any other actions that may independently break?</li>
<li>What are the server settings?</li>
<li>What version of the code is running?</li>
</ul>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.<br />
<br />
Here's how I've accomplished this on a <a href="http://rubyonrails.org/">Ruby on Rails</a> project.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Single Location</span><br />
First, I created a controller with a single page:<br />
<pre style="background: #000000; color: #d1d1d1;"><span class="Apple-style-span" style="font-family: monospace;">prompt<span style="color: #d2cd86;">></span> rails generate controller Admin index</span></pre>Here is the beginning of the controller class.<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">class</span> AdminController < ApplicationController
before_filter :require_admin
<span style="color: #e66170; font-weight: bold;">def</span> index
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>As you can see, I've protected it, so only administrative users can view this page. Here is the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">views/admin/index.html.haml</span> where I show server state.<br />
<pre style="background: #000000; color: #d1d1d1;">This is the status page<span style="color: #d2cd86;">.</span>
<span style="color: #d2cd86;">%</span>br
<span style="color: #d2cd86;">%</span>h2 Version
<span style="color: #d2cd86;">%</span>pre<span style="color: #d2cd86;">=</span> <span style="color: #d2cd86;">@</span>version
<span style="color: #d2cd86;">%</span>h2 Environment
<span style="color: #d2cd86;">%</span>br
<span style="color: #d2cd86;">%</span>b RAILS_ENV<span style="color: #b060b0;">:</span>
<span style="color: #d2cd86;">=</span> ENV<span style="color: #d2cd86;">[</span><span style="background-attachment: initial; background-clip: initial; background-color: #dd0000; background-image: initial; background-origin: initial; color: white;">'RAILS_ENV'</span><span style="color: #d2cd86;">]</span>
<span style="color: #d2cd86;">%</span>br
<span style="color: #d2cd86;">%</span>hr
<span style="color: #d2cd86;">%</span>h2 Site Tests
<span style="color: #d2cd86;">.</span>test
<span style="color: #d2cd86;">=</span> button_to <span style="color: #02d045;">"</span><span style="color: #00c4c4;">Test DB</span><span style="color: #02d045;">"</span><span style="color: #d2cd86;">,</span> <span style="color: #b060b0;">{</span><span style="color: #b060b0;">:</span>action<span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">></span><span style="color: #02d045;">"</span><span style="color: #00c4c4;">test_db</span><span style="color: #02d045;">"</span><span style="color: #b060b0;">}</span><span style="color: #d2cd86;">,</span> <span style="color: #b060b0;">:</span>remote<span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">></span>true<span style="color: #d2cd86;">,</span> <span style="color: #b060b0;">:</span>onClick<span style="color: #d2cd86;">=</span><span style="color: #d2cd86;">></span><span style="color: #02d045;">"</span><span style="color: #00c4c4;">startTest()</span><span style="color: #02d045;">"</span>
<span style="color: #d2cd86;">%</span>hr
<span style="color: #d2cd86;">%</span>h2 Test Output
<span style="color: #008073;">#testOutput</span>
<span style="color: #b060b0;">:</span>javascript
function startTest<span style="color: #d2cd86;">(</span><span style="color: #d2cd86;">)</span> <span style="color: #b060b0;">{</span>
$<span style="color: #d2cd86;">(</span><span style="color: #02d045;">"</span><span style="color: #00c4c4;">#testOutput</span><span style="color: #02d045;">"</span><span style="color: #d2cd86;">)</span><span style="color: #d2cd86;">.</span>replaceWith<span style="color: #d2cd86;">(</span><span style="background-attachment: initial; background-clip: initial; background-color: #dd0000; background-image: initial; background-origin: initial; color: white;">'<div id="testOutput">Loading...</div>'</span><span style="color: #d2cd86;">)</span><span style="color: #b060b0;">;</span>
<span style="color: #b060b0;">}</span>
</pre><br />
<span class="Apple-style-span" style="font-size: x-large;">Dependency Tests</span><br />
I've implemented each test as a separate Ajax call. In the view code above, this everything from "<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Site Tests</span>" 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">.test</span> div. As you can see in the code above, the button makes an Ajax call (<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">:remote=>true</span>) and calls a javascript function to show that the result is loading. It is up to the controller to then update the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">testOutput</span> div with output status.<br />
<br />
In my <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">admin_controller.rb</span> I have a function for each button that looks like:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">def</span> test_db
standard_test <span style="color: #e66170; font-weight: bold;">do</span>
<span style="color: #9999a9;"># insert test code here</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>The helper function <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">standard_test</span> looks like:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">def</span> standard_test
<span style="color: #e66170; font-weight: bold;">begin</span>
<span style="color: #e66170; font-weight: bold;">yield</span>
@success <span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">true</span>
<span style="color: #e66170; font-weight: bold;">rescue</span> Exception <span style="color: #d2cd86;">=</span>> e
@success <span style="color: #d2cd86;">=</span> <span style="color: #e66170; font-weight: bold;">false</span>
@exception <span style="color: #d2cd86;">=</span> e
<span style="color: #e66170; font-weight: bold;">end</span>
respond_to <span style="color: #e66170; font-weight: bold;">do</span> |<span style="color: #e66170; font-weight: bold;">format</span>|
<span style="color: #e66170; font-weight: bold;">format</span><span style="color: #d2cd86;">.</span>html <span style="color: #b060b0;">{</span>
flash<span style="color: #d2cd86;">[</span>:error<span style="color: #d2cd86;">]</span> <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"This should've been ajax"</span>
redirect_to admin_index_path
<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">format</span><span style="color: #d2cd86;">.</span>js <span style="color: #b060b0;">{</span>
render <span style="color: #00c4c4;">'test_output'</span>, :layout<span style="color: #d2cd86;">=</span>><span style="color: #e66170; font-weight: bold;">false</span>
<span style="color: #b060b0;">}</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre>All it does is executes the test code (via the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">yield</span> 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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">test_output.js.haml</span> which is simply:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #d2cd86;">!=</span> <span style="color: #00c4c4;">"$('#testOutput').replaceWith('#{escape_javascript render(:partial=>'test_output')}')"</span>
</pre>As you can see, this merely replaces the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">testOutput</span> div with what is rendered by the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">_test_output.html.haml</span> partial:<br />
<pre style="background: #000000; color: #d1d1d1;">#testOutput
- if @success
%b SUCCESS
- else
%b <span style="color: #e66170; font-weight: bold;">ERROR</span>
%br
%b <span style="color: #e66170; font-weight: bold;">Error</span> <span style="color: #e66170; font-weight: bold;">Class</span>:
<span style="color: #d2cd86;">=</span> @exception.class
%br
%b <span style="color: #e66170; font-weight: bold;">Error</span> Message:
%<span style="color: #e66170; font-weight: bold;">pre</span><span style="color: #d2cd86;">=</span> @exception.message
%b <span style="color: #e66170; font-weight: bold;">Error</span> Stack Trace:
%<span style="color: #e66170; font-weight: bold;">pre</span>
- @exception.backtrace.each do |line|
<span style="color: #d2cd86;">=</span> line
%b <span style="color: #e66170; font-weight: bold;">Error</span> Inspect
%<span style="color: #e66170; font-weight: bold;">pre</span><span style="color: #d2cd86;">=</span> @exception.inspect</pre>Of course, don't forget to make sure your routes are in your <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">config/routes.rb.</span><br />
<pre style="background: #000000; color: #d1d1d1;">get <span style="color: #00c4c4;">'admin/index'</span>
post <span style="color: #00c4c4;">'admin/my_test'</span> <span style="color: #9999a9;"># separate route for each test</span></pre><span class="Apple-style-span" style="font-size: x-large;">Version</span><br />
Including version information is going to be very specific to your environment. We use <a href="http://mercurial.selenic.com/">Mercurial</a> for our version control, and since we have a number of Java project, we use <a href="http://ant.apache.org/">ant</a> for our deployment process. Here is how I connected these together.<br />
<br />
In the ant deployment script, I added a target that looks like:<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #ff8906;"><</span><span style="color: #f6c1d0;">target</span> name<span style="color: #d2cd86;">=</span><span style="color: #00c4c4;">"</span><span style="color: #00c4c4;">write.hg.version</span><span style="color: #00c4c4;">"</span><span style="color: #ff8906;">></span>
<span style="color: #ff8906;"><</span><span style="color: #f6c1d0;">exec</span> executable<span style="color: #d2cd86;">=</span><span style="color: #00c4c4;">"</span><span style="color: #00c4c4;">hg</span><span style="color: #00c4c4;">"</span> dir<span style="color: #d2cd86;">=</span><span style="color: #00c4c4;">"</span><span style="color: #00c4c4;">${project}</span><span style="color: #00c4c4;">"</span> output<span style="color: #d2cd86;">=</span><span style="color: #00c4c4;">"</span><span style="color: #00c4c4;">${project}/config/version.txt</span><span style="color: #00c4c4;">"</span><span style="color: #ff8906;">></span>
<span style="color: #ff8906;"><</span><span style="color: #f6c1d0;">arg</span> value<span style="color: #d2cd86;">=</span><span style="color: #00c4c4;">"</span><span style="color: #00c4c4;">summary</span><span style="color: #00c4c4;">"</span> <span style="color: #ff8906;">/></span>
<span style="color: #fb8400;"></</span><span style="color: #f6c1d0;">exec</span><span style="color: #fb8400;">></span>
<span style="color: #fb8400;"></</span><span style="color: #f6c1d0;">target</span><span style="color: #fb8400;">></span>
</pre>All that is left is to get the contents of this <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">version.txt</span> file into the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">@version</span> variable that is displayed by <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">index.html.haml</span>. This is done by simply having the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">index</span> method in <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">admin_controller.rb</span> read in the file.<br />
<pre style="background: #000000; color: #d1d1d1;"><span style="color: #e66170; font-weight: bold;">def</span> index
name <span style="color: #d2cd86;">=</span> File<span style="color: #d2cd86;">.</span>join<span style="color: #d2cd86;">(</span>RAILS_ROOT, <span style="color: #00c4c4;">'config'</span>, <span style="color: #00c4c4;">'version.txt'</span><span style="color: #d2cd86;">)</span>
<span style="color: #e66170; font-weight: bold;"> if</span> File<span style="color: #d2cd86;">.</span>exists? name
f <span style="color: #d2cd86;">=</span> File<span style="color: #d2cd86;">.</span><span style="color: #e66170; font-weight: bold;">new</span><span style="color: #d2cd86;">(</span>name<span style="color: #d2cd86;">)</span>
@version <span style="color: #d2cd86;">=</span> f<span style="color: #d2cd86;">.</span>read
<span style="color: #e66170; font-weight: bold;">else</span>
@version <span style="color: #d2cd86;">=</span> <span style="color: #00c4c4;">"Unknown version"</span>
<span style="color: #e66170; font-weight: bold;">end</span>
<span style="color: #e66170; font-weight: bold;">end</span>
</pre><span class="Apple-style-span" style="font-size: x-large;">Conclusion</span><br />
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 <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">standard_test</span> method, new tests can consist of just the code they are intended to test.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-68824093343214768412011-07-18T09:00:00.000-04:002011-07-18T09:00:20.995-04:00Windows Command Line Cut & Paste RantJust 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. 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.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-o0u2ZfC-nxA/TiGYMU5pHnI/AAAAAAAABA4/8W4Ev37DFbo/s1600/CommandMark.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-o0u2ZfC-nxA/TiGYMU5pHnI/AAAAAAAABA4/8W4Ev37DFbo/s1600/CommandMark.jpg" /></a></div><br />
Is there a time that this behavior is useful? If you know, please share it with me.<br />
<br />
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.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-oI50eTXVQUk/TiGYdIrYlFI/AAAAAAAABA8/ry24TouFVeA/s1600/notepadCutPaste.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-oI50eTXVQUk/TiGYdIrYlFI/AAAAAAAABA8/ry24TouFVeA/s1600/notepadCutPaste.jpg" /></a></div><br />
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?<br />
<br />
(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.)Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com4tag:blogger.com,1999:blog-5724099066106449846.post-15410057239497600972011-07-11T09:00:00.000-04:002011-07-11T09:00:13.227-04:00Attack of the ClonesAs I <a href="http://madkingsmusings.blogspot.com/2011/06/too-slow.html">mentioned</a>, I was recently at <a href="http://2011.icse-conferences.org/">ICSE</a>. 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 <a href="http://2011.icse-conferences.org/content/frequency-and-risks-changes-clones">measure how bad clones really are</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><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;"><img border="0" height="179" src="http://i452.photobucket.com/albums/qq250/Demonic-Genius/stormtrooperarmy.jpg" width="320" /></a></div>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.<br />
<br />
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: <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>. 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.<br />
<br />
So how bad are they? Well, as I mentioned above, there was <a href="http://portal.acm.org/citation.cfm?id=1985836">research</a> 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.<br />
<br />
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.<br />
<br />
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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com0tag:blogger.com,1999:blog-5724099066106449846.post-5058451795828284372011-07-04T09:00:00.014-04:002011-07-04T09:00:12.368-04:00GCJ 2011 T-ShirtDue to the holiday weekend, I am just making a short post this week.<br />
<br />
Last week I mentioned that I got knocked out of the <a href="http://community.topcoder.com/tco11/algorithm/">TCO 2011</a> competition, and so will get no shirt for it. Well, a couple days later FedEx delivered my <a href="http://code.google.com/codejam/">Google Code Jam</a> 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 <a href="http://madkingsmusings.blogspot.com/2011/06/too-slow.html?showComment=1308329901852#c901489557281530445">a picture was requested</a>, here it is. And, no Alan, programming contests don't completely eliminate the <a href="http://madkingsmusings.blogspot.com/2011/06/no-t-shirt-for-me.html?showComment=1309229634512#c3433002833194622196">need to go clothes shopping</a>, though I do have around two dozen t-shirts plus a nice winter coat and hat from various programming contests.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><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;"><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" /></a></div>Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com1tag:blogger.com,1999:blog-5724099066106449846.post-57874585839943133842011-06-27T09:00:00.002-04:002011-06-27T09:00:10.728-04:00No T-Shirt For MeThis past Saturday my brain turned off and I failed to get even a single problem right in the 2<sup>nd</sup> round of the <a href="http://community.topcoder.com/tco11/algorithm/">TCO 2011</a>. This was the round you had to get past to get a t-shirt.<br />
<br />
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, <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">if you don't know how to do something, you don't know how to do it with a computer</a>. 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.<br />
<br />
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.<br />
<br />
<span class="pullout">walk away from the problem</span>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 <a href="http://madkingsmusings.blogspot.com/2007/03/no-really-dont-get-stuck-in-rut.html">stay in that rut</a> 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 <a href="http://madkingsmusings.blogspot.com/2010/10/tell-it-to-bear.html">talk through the problem with an external party</a>. 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.<br />
<br />
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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com3tag:blogger.com,1999:blog-5724099066106449846.post-84044789720842834192011-06-20T09:00:00.001-04:002011-06-20T09:00:11.969-04:00Testing in Production<div class="separator" style="clear: both; text-align: center;"><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;"><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" /></a></div>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.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">So what happened?</span><br />
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?<br />
<br />
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?<br />
<br />
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, <a href="http://www.codinghorror.com/blog/2009/12/version-1-sucks-but-ship-it-anyway.html">release your buggy software</a>. 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.<br />
<br />
<span class="pullout">Releasing finds missing requirements</span>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.<br />
<br />
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.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com2tag:blogger.com,1999:blog-5724099066106449846.post-85957158390255387582011-06-14T09:00:00.029-04:002011-06-14T09:00:07.732-04:00Too Slow<div class="separator" style="clear: both; text-align: center;"><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;"><img border="0" height="191" src="http://3.bp.blogspot.com/-sqrHicya1yQ/Tfa4sQ3t_YI/AAAAAAAAAsk/9_B8XC4S-Fo/s400/me_surf.jpg" width="400" /></a></div>I have been remiss in getting my posts out. A few weeks ago I was at <a href="http://2011.icse-conferences.org/">ICSE</a>. I had intended on making a post based on that conference, but as soon as it was over, I went into vacation mode. <a href="http://www.hawaiistateparks.org/parks/oahu/index.cfm?park_id=15">While on vacation</a> <a href="http://www.govisithawaii.com/2009/01/09/kayaking-the-mokulua-islands-with-kailua-sailboards-kayaks/">I found</a> <a href="http://www.best-of-oahu.com/lanikai-pillbox-hike.html">a number</a> of <a href="http://www.bishopmuseum.org/">things to do</a> that <a href="http://www.sunsetsurattsurfschool.com/">kept me away</a> from computers. About the only computer thing I did do was compete in the <a href="http://code.google.com/codejam/contest/dashboard?c=1150486">2<sup>nd</sup> round of the Google Code Jam</a>.<br />
<br />
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 <a href="http://maps.google.com/?ie=UTF8&ll=-2.635789,-165.058594&spn=91.105943,109.160156&z=3">great big ocean</a> 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 <a href="http://code.google.com/codejam/contest/scoreboard?c=1150486#sp=321">well enough to advance</a> to the next round (top 500).<br />
<br />
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 <a href="http://code.google.com/codejam/contest/dashboard?c=1158485">3<sup>rd</sup> round of the Google Code Jam</a> where they would select the top 25. Unfortunately, that was for naught, as <a href="http://code.google.com/codejam/contest/scoreboard?c=1158485#vt=1&sp=261">I did not advance</a>.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Too Slow</span><br />
24 out of 25 of the advancers solved the first 3 problems, as well as the small input set on the 4<sup>th</sup> problem. I, also, know how to solve the first 3 problems as well as the small input set on the 4<sup>th</sup> 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<sup>th</sup> place was submitting his final solution.<br />
<br />
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 knocked me out of the running to advance. On the third problem, bugs were again my downfall. I didn't take my <a href="http://madkingsmusings.blogspot.com/2011/04/programming-contest-season.html">own advice</a> 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.<br />
<br />
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.<br />
<br />
As for why this post is a day late... well, I am just a little bit slow.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com3tag:blogger.com,1999:blog-5724099066106449846.post-21833724219607265222011-05-23T09:00:00.002-04:002011-05-23T09:00:12.567-04:00Moore's Law Doesn't Catch ImaginationBack in 1992 I had an opportunity to use an <a href="http://en.wikipedia.org/wiki/Intel_iPSC/860">iPSC super computer</a>. 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.<br />
<br />
Using a generalization of <a href="http://en.wikipedia.org/wiki/Moore's_law">Moore's Law</a> 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 <a href="http://en.wikipedia.org/wiki/Watson_(computer)">supercomputers with terabytes of RAM</a>, and the <a href="http://madkingsmusings.blogspot.com/2011/03/visiting-foreign-land.html">laptop I just bought</a> has 8GB of RAM, which is 4,000 times what was <a href="http://www.zdnet.com/blog/perlow/1991s-pc-technology-was-unbelievable/16413">common then</a>. I am actually kind of amazed at how accurate this rule of thumb is.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div><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;"><img border="0" height="100" src="http://4.bp.blogspot.com/-pqJvZUsy3tk/TcG-1AaV35I/AAAAAAAAAsg/cXTnsYu-PkI/s200/beachball2.png" width="100" /></a>However, my programming goals apparently grow just as fast. Just the other day, as I was <a href="http://madkingsmusings.blogspot.com/2011/04/programming-contest-season.html">practicing for programming contests</a>, 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.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;">Conclusion</span><br />
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. Modern computers are powerful, but they still aren't as powerful as we'd all like.Michael Haddox-Schatzhttp://www.blogger.com/profile/02782657000665338948noreply@blogger.com6