<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2813261110497121234</id><updated>2011-08-16T19:59:42.723-07:00</updated><category term='ruby'/><category term='anaemic domain objects'/><category term='stubba'/><category term='asynchronous'/><category term='recording helper'/><category term='make constructor protected'/><category term='DRY'/><category term='ActiveMessaging'/><category term='ActiveMQ'/><category term='standard_params'/><category term='thoughtworks'/><category term='textmate'/><category term='readability of tests'/><category term='hiring'/><category term='ruby developer'/><category term='reliable'/><category term='moist tests'/><category term='module'/><category term='focused unit test'/><category term='service objects'/><category term='rails'/><category term='stub_object'/><category term='with_constants'/><category term='jruby'/><category term='readability'/><category term='testing'/><category term='jms'/><category term='asynchronous messaging'/><category term='ActiveMessaging ActiveMQ asynchronous messaging jms jruby rails'/><title type='text'>Beech Bonanza</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-594027810782745162</id><published>2008-11-19T19:30:00.000-08:00</published><updated>2008-11-19T19:42:30.491-08:00</updated><title type='text'>State Pattern using module extension</title><content type='html'>I try to favour delegation over inheritance. But sometimes Replace Inheritance with Delegation can be difficult. It turns out that when you have an inheritance heirarchy, and state is used in both the superclass and subclasses, it can be difficult to remove the inheritance heirarchy and replace it with delegation. Let's look at an example. Here we're modeling bikes:&lt;br /&gt; &lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;wheel_circumference&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class="support support_class support_class_ruby"&gt;Math&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_other punctuation_separator_other_ruby"&gt;::&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;PI&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;wheel_diameter&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_diameter&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;FrontSuspensionMountainBike&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Bicycle&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;off_road_ability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_diameter&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;TIRE_WIDTH_FACTOR&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;front_fork_travel&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FRONT_SUSPENSION_FACTOR&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;RigidMountainBike&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Bicycle&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;off_road_ability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_diameter&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;TIRE_WIDTH_FACTOR&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;                                                        &lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;RoadBike&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Bicycle&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;off_road_ability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;raise&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;You can't take a road bike off-road&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;In this heirarchy the @tire_diameter instance variable is used in both the superclass and subclass. You can imagine that if we were to try to have the Bicycle class delegate to a FrontSuspensionMountainBike object, we'd have to duplicate the @tire_diameter state. This becomes a bit awkward, particularly if @tire_diameter can change - you'd have to ensure that the the @tire_diameter in Bicycle is kept in synch with the one in FrontSuspensionMountainBike. I'd probably decide that it wasn't worth the effort, and keep the inheritance heirarchy.&lt;br /&gt;&lt;br /&gt;But what if we wanted to change the type of bike at run-time? Perhaps we want to upgrade a RigidMountainBike (a bike with no suspension) to a FrontSuspensionMountainBike. Using the state pattern would be ideal, but the traditional state pattern uses delegation. With ruby modules we have a different option. Rather than represent the FrontSuspensionMountainBike and RigidMountainBike behaviour as subclasses of Bicycle, we could make them modules and extend Bicycle with the appropriate module for the behviour that we want:      &lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;mountain_bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;br /&gt;front_suspension_bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;becomes&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;mountain_bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;extend&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;front_suspension_bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;extend&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;wheel_circumference&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class="support support_class support_class_ruby"&gt;Math&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_other punctuation_separator_other_ruby"&gt;::&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;PI&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;wheel_diameter&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_width&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_module meta_module_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_module keyword_control_module_ruby"&gt;module&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_module entity_name_type_module_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;off_road_ability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_width&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;TIRE_WIDTH_FACTOR&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;front_fork&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;travel &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FRONT_SUSPENSION_FACTOR&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_module meta_module_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_module keyword_control_module_ruby"&gt;module&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_module entity_name_type_module_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;off_road_ability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;tire_width&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;TIRE_WIDTH_FACTOR&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;                                                        &lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So we could conceivably upgrade our mountain bike at run-time to add a fork with front suspension:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;extend&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;...&lt;/span&gt;&lt;br /&gt;bike&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;add_front_suspension&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;fork&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_module meta_module_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_module keyword_control_module_ruby"&gt;module&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_module entity_name_type_module_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;add_front_suspension&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;fork&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;front_fork&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; fork&lt;br /&gt;    &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;extend&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;So we now have the ability to change behaviour at run-time, and we haven't introduced any duplication - the state and behaviour is still shared between the Bicycle class and the modules. But there's a problem:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;bike&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;kind_of?&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant constant_language constant_language_ruby"&gt;true&lt;/span&gt;    &lt;br /&gt;bike&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;kind_of?&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant constant_language constant_language_ruby"&gt;true&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;We were able to mix in the FrontSuspensionMountainBike behaviour, but the RigidMountainBike behaviour still exists on the bike object. It turns out that ruby doesn't provide the ability to unmix a module. But all is not lost - there's an open-source library called &lt;a href="http://www.somethingnimble.com/bliki/mixology"&gt;mixology&lt;/a&gt; that does exactly what we want.&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;&lt;span class="meta meta_require meta_require_ruby"&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;require&lt;/span&gt; &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;mixology&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_module meta_module_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_module keyword_control_module_ruby"&gt;module&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_module entity_name_type_module_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;add_front_suspension&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;fork&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;front_fork&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; fork&lt;br /&gt;    unmix&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;    mixin&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;FrontSuspensionMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;                                     &lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;It provides two methods: unmix (for removing a module from an object) and mixin, for adding a module to an object. Our bug is now fixed:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby"&gt;bike &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Bicycle&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;extend&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;...&lt;/span&gt;&lt;br /&gt;bike&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;add_front_suspension&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;fork&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;bike&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;kind_of?&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RigidMountainBike&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant constant_language constant_language_ruby"&gt;false&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-594027810782745162?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/594027810782745162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=594027810782745162' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/594027810782745162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/594027810782745162'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/11/state-pattern-using-module-extension.html' title='State Pattern using module extension'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-4092625127207997380</id><published>2008-11-18T15:08:00.000-08:00</published><updated>2009-01-25T18:24:53.057-08:00</updated><title type='text'>Bored with the rhetoric</title><content type='html'>During election time, I often find myself contemplating argumentative techniques as I watch the politicians do battle for votes. But it's not electioneering that I'm sick of - when you don't have a TV, live in the mountains, and avoid USA Today, you can moderate your intake pretty easily. It's programmer rhetoric that's gotten me down. We're an opinionated bunch, we developers. I'd like to think there's some relationship between an enjoyment of discrete mathematics and a healthy passion for the subtleties of logical argument, but rationality doesn't always reign supreme. Here's a few examples that I've seen over the past year:&lt;br /&gt;&lt;br /&gt;Using the phrases "I'm just being pragmatic", or "my solution is the simplest thing that works" to convince someone the merits of an idea. &lt;br /&gt;If you respect the person you are arguing with, and you are both aiming for a pragmatic and simple solution (as we all should be), then these statements are not helping. You're trying to discredit the other person's argument by implying that they are not pragmatic, or their solution is too complex. If the two of you agree that one solution is simpler than the other, then focus on why the simpler solution is adequate. &lt;br /&gt;                                                                                                                                   &lt;br /&gt;"But that's not Agile"&lt;br /&gt;I see a repeating pattern in software. Over time, we learn from our mistakes. Out of this learning, we come up with some high level goals (such as the four specified in the Agile manifesto). We then develop some set of practices to help guide people in achieving the goals. Then we follow the practices, refining them a bit to make sure the goals are met. But then, after time, we get caught up in the mechanics of the practices, and forget what the goals are all about. Practices are often much easier to understand. Often when I hear "but that's not Agile", it's to do with a practice. It might be "If a story is more than 3 lines long, we're not being Agile". But the manifesto goal is "Customer collaboration over contract negotiation". So long as we're not sacrificing customer collaboration with our 4th line on our story, then it shouldn't be a problem. And if you think we're hurting our customer collaboration, focus on that in the argument. &lt;br /&gt;&lt;br /&gt;The Logical Fallacy&lt;br /&gt;"see, that's why we need continuous integration"&lt;br /&gt;This quote comes from me. I said it. I use programmer rhetoric too :( But I'm trying to get better... Anyway, I was arguing with my project manager about our need for continuous integration. We were developing on macs, and deploying to linux. We had a problem that was only exposed on linux. I had been advocating for a linux machine to run Cruise Control. So I jumped on the chance to further my argument. "See, that's why we need continuous integration". But my project manager called me out on it. He's a smart guy. And he said "So are you telling me that you would have had a test that exposed this problem?". I had to admit that no, I wouldn't have. I'd used a logical fallacy. Yes, continuous integration helps expose bugs. Yes, we had a bug. Both of those statements are logically correct. But it does not follow that continuous integration would have exposed this bug. Shame on me...&lt;br /&gt;&lt;br /&gt;As Steven Levitt says in &lt;a href="http://www.amazon.com/Freakonomics-Revised-Expanded-Economist-Everything/dp/0061234001"&gt;Freakonmics&lt;/a&gt;, professionals tend to take advantage of their specialised knowledge to better themselves. He's talking specifically about realtors in one chapter, but programmers aren't above this behaviour. The skills and knowledge that we have gained in order to do our jobs is pretty rare, and we're privileged to be in the position we're in. But all too often I see developers taking advantage of their specialised knowledge during an argument and misleading their opponent by talking about something that the opponent doesn't understand. If you want to win the argument, explain your reasoning in a language that your adversary understands.&lt;br /&gt;&lt;br /&gt;And yes, I do understand the irony of using rhetoric to describe my frustration with rhetoric. But I swear it's for good, not evil.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-4092625127207997380?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/4092625127207997380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=4092625127207997380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/4092625127207997380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/4092625127207997380'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/11/bored-with-rhetoric.html' title='Bored with the rhetoric'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-2369376782788964337</id><published>2008-09-24T17:18:00.001-07:00</published><updated>2008-09-24T17:20:26.766-07:00</updated><title type='text'>Discount for Voices That Matter Ruby Conference</title><content type='html'>I'll be speaking at the &lt;a href="http://www.voicesthatmatter.com/ruby2008/"&gt;Voices That Matter Professional Ruby Conference&lt;/a&gt;. You can get a discount of $200 if you book using this code: PRDPKRL.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-2369376782788964337?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/2369376782788964337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=2369376782788964337' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2369376782788964337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2369376782788964337'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/09/discount-for-voices-that-matter-ruby.html' title='Discount for Voices That Matter Ruby Conference'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-3156756218474394876</id><published>2008-09-24T16:46:00.000-07:00</published><updated>2008-09-24T16:56:36.980-07:00</updated><title type='text'>soylent green is people. so is software</title><content type='html'>I did a presentation on pair programming at a client a while back and it caused me to reflect a bit on the virtues of pairing. During my research, as is often the case for me, I found someone that articulated the benefits (and concerns) far better than I could. He may not have had pair programming in mind, but it is amazingly apt:&lt;br /&gt;&lt;br /&gt;"Only if the various principles - names, definitions, intimations and perceptions - are laboriously tested and rubbed one against the other in a reconciliatory tone, without ill will during the discussion, only then will insight and reason radiate forth in each case, and achieve what is for man the highest possible force..." - Plato &lt;br /&gt;&lt;br /&gt;Despite the somewhat sexist use of the word 'man' as a synonym the human race, Plato has done a good job here of describing the process of argument and some of the things to watch out for. The section that resonated most with me was the notion of perceptions being "laboriously tested and rubbed one against the other". This is where the true advantage of pair programming comes to the fore. By rubbing one idea against another, new ideas are born... is there a euphemism there? Anyway, the point is that you may start out with a couple of different ideas, but the process of discussion will often modify these ideas, combine them, or spawn completely new ideas. I've even had misunderstandings inspire orthogonal solutions. One participant will explain an idea, and the second participant will misunderstand, but try to build on their (mis) understanding, sometimes even coming up with a superior design.&lt;br /&gt;&lt;br /&gt;But you don't need to take my word for it. A &lt;a href="http://collaboration.csc.ncsu.edu/laurie/Papers/XPSardinia.PDF"&gt;study&lt;/a&gt; undertaken by Alistair Cockburn and Laurie Williams highlighted the benefits of pair programming. During their research, they found that pair programming improves design quality, reduces defects, reduces staffing risk, enhances technical skills through knowledge transfer and improves team communication. It costs about 15% more in development time (not double, as one might intuit). The cost in extra development time is more then compensated for (by at least an order of magnitude) with savings in reduced defects. &lt;br /&gt;&lt;br /&gt;Pair programming was also considered a more enjoyable method of development by the participants in this study. I certainly find it more enjoyable, though I can understand that some software developers don't. If you're a personality who doesn't enjoy constant communication and collaboration then pairing can be tiresome. But, as my friend &lt;a href="http://www.somethingnimble.com/collaborators/zak"&gt;z&lt;/a&gt; once wrote "Soylent Green is people. So is software". Many difficulties associated with the software development process stem from poor communication, and pair programming can go a long way to addressing these problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-3156756218474394876?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/3156756218474394876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=3156756218474394876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/3156756218474394876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/3156756218474394876'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/09/soylent-green-is-people-so-is-software.html' title='soylent green is people. so is software'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-7814031282240379422</id><published>2008-09-24T16:39:00.000-07:00</published><updated>2008-09-24T16:46:24.560-07:00</updated><title type='text'>Refactoring: Ruby Edition is now available on Safari</title><content type='html'>Refactoring: Ruby Edition is now available on Safari as a &lt;a href="http://my.safaribooksonline.com/9780321603968"&gt;Rough Cut&lt;/a&gt;. It's also available for pre-order on &lt;a href="http://www.amazon.com/Refactoring-Ruby-Addison-Wesley-Professional/dp/0321603508"&gt;Amazon&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-7814031282240379422?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/7814031282240379422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=7814031282240379422' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7814031282240379422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7814031282240379422'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/09/refactoring-ruby-edition-is-now.html' title='Refactoring: Ruby Edition is now available on Safari'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-7444119380546155322</id><published>2008-01-27T20:18:00.000-08:00</published><updated>2008-01-27T20:22:11.288-08:00</updated><title type='text'>The evolution of a Domain in rails: Part 2 Separate Query From Modifier</title><content type='html'>When working on Refactoring, Ruby Edition, I realised that Separate Query From Modifier was one of my favourite refactorings. I'd been doing it for a while without realising that it had a formal name. I probably hadn't realised the full benefits of it either. From Refactoring, Ruby Edition: "When you have a function that gives you a value and has no observable side effects, you have a very valuable thing. You can call this function as often as you like. You can move the call to other places in the method. In short, you have a lot less to worry about." If you do not separate the querying code from the modifying code, code becomes difficult to understand and re-use. When I'm trying to track down a bug, I'm looking for two things: 1. The code that triggers the offending code (the query), and 2. the offending code itself (the modifier). If I have to search through a method that makes a query, then does some modification, then does another query, and some more modification, then my head starts to hurt. Which query returns the result that triggers the bug? And which modification is the bad modification?  If we can separate the querying code from the modifying code, we can often achieve a better abstraction of our business rules and promote re-use.  As a project evolves, we often have to introduce new trigger points for state changes. And in an agile environment, I often see the story cards evolve like this:&lt;br /&gt;&lt;br /&gt;Story 1:  Under condition 'X', 'A' should change such that...&lt;br /&gt;Story 2:  Under condition 'X', warn the user before making the change to 'A'&lt;br /&gt;&lt;br /&gt;As Agile developers, we like this kind of story breakdown. After completing story 1, we can demonstrate to the user that we understand condition 'X' and the changes that should be made to 'A'.  If we get it wrong, then we can fix it.  Story 2 is the icing on the cake. It provides some nicer usability around the feature. It might also be a lower priority, and we might be able to release the code without Story 2 and gain some real business value before we polish it later. So the separation of the stories could be important. But if we mix query and modifier, it can become very difficult to introduce the warning, or introduce new trigger points for the desired state change.&lt;br /&gt;&lt;br /&gt;But I've realised that Separate Query From Modifier is not always as easy as extracting conditional logic to one method, and having the modifying logic in another method. In my last post I said that in Rails, it can sometimes be difficult to re-wire multiple ActiveRecord objects of different type together according to some set of business rules without using the database as a storage mechanism, and without the validations getting in your way. We've often ended up with complex service methods that perform a query, do some modification (saving to the database), perform another query, do some more modification, and so on.  You might end up with 5 or 6 queries that have to be performed in sequence. Later queries might depend on modifications that have been performed as a result of earlier queries. And the code is ugly, and difficult to re-use.&lt;br /&gt;&lt;br /&gt;One way that we've solved this problem is to create a results object that represents the new relationships to be created.  It's just a plain old Ruby object with some attributes.  Let's say we're trying to build a new 'A' object, with relationships to B and C.  Let's say A has_many B, and A has_one C.  I'd create a results object called NewA, with an array attribute for the Bs and an attribute for the C.  As I go through my algorithm, I can add my Bs and my C to the results object - without actually changing any underlying associations.  (I now have a query without the modifier). Toward the end of the algorithm, I can present my results object to the user for confirmation, and if they confirm the change, then I can grab my results object and make the actual associations in the database (the modifier).  I might even find that I can move some behaviour to this results object, and it will cease being a dumb data object.  But even if I don't get to move any behaviour, the separation of query and modifier is worth the effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-7444119380546155322?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/7444119380546155322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=7444119380546155322' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7444119380546155322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7444119380546155322'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/01/evolution-of-domain-in-rails-part-2.html' title='The evolution of a Domain in rails: Part 2 Separate Query From Modifier'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-6305634278947771311</id><published>2008-01-25T08:44:00.000-08:00</published><updated>2008-01-25T09:00:24.466-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='service objects'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='recording helper'/><category scheme='http://www.blogger.com/atom/ns#' term='anaemic domain objects'/><title type='text'>The evolution of a Domain in rails: Part 1</title><content type='html'>I've been at my current client for about 15 months now, and during that time we've been working in fairly complex domains.  Whilst our application started out very CRUD-like, it gradually moved away from CRUD in its simplest form (resources with simple attributes) to quite complex domain models.  We now have quite a few functions that cause the interaction of 10-plus domain objects and often require the re-wiring of these objects according to a change in state triggered by a user.  I was recently asked by a colleague to explain the limitations of &lt;a href="http://wiki.rubyonrails.org/rails/pages/ActiveRecord"&gt;ActiveRecord&lt;/a&gt; and some ways that we've overcome those issues.  His concern was that Rails' tight coupling to the database would lead to anaemic domain objects that are effectively just DTO's, with all of the business logic creeping up into the view.&lt;br /&gt;&lt;br /&gt;We've never ended up with business logic in the view, but we have made mistakes such that this behaviour has ended up in non-ideal parts of the application.  Our first mistake was to place too much business logic in the controllers.  This came about when we were trying to orchestrate the interactions between (say) three domain objects of different type.  We asked ourselves "This behaviour doesn't belong on any one of the three domain objects, so where should it go?".  Rails makes a clear distinction between Model, View, and Controller, and most of the examples only show "model" objects as being those that inherit from ActiveRecord::Base (and are therefore stored in a database).  So given that we didn't want this business logic in the view, and it didn't belong on any of the "model" objects that we already had, the only place left was the controller.  So our controllers got fat.  And they were hard to test.  And hard to change.  And the business logic was very hard to re-use.  And our model objects were thin (you might even say anaemic).  Boo.&lt;br /&gt;&lt;br /&gt;Fresh from reading &lt;a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215"&gt;Domain Driven Design&lt;/a&gt;, our teammate Pat Sarnacke came to the rescue, saying "We need services".  And it's interesting to note that we were &lt;a href="http://blog.jayfields.com/2007/10/rails-rise-fall-and-potential-rebirth.html"&gt;not alone&lt;/a&gt; in this discovery.  So we extracted the business logic from our complex controller actions into Service objects. (From Domain Driven Deisgn, service objects are objects "with no state of their own nor any meaning in the domain beyond the operation they host").  We got quite a bit of re-use of this logic by doing so.  It was definitely a step in the right direction.  But then our Services started to grow.  They became large and complex, and although they displayed the positive trait of having low coupling (with perhaps only one public method), they weren't very cohesive. We extracted private methods that were cohesive within themselves, but the collection of private methods made no sense together, other than the fact that they were part of the "procedure" of the service.  And so the service became hard to understand in isolation, and not particularly amenable to re-use.  If you wanted to re-use the service as a whole, then you were in good shape (much better than when the logic was in the controller).  But if you wanted to re-use one of the private methods, it wasn't so easy (and not simply because they were private).  &lt;br /&gt;&lt;br /&gt;It turns out that, in some cases, it can be difficult to re-wire multiple ActiveRecord objects of different type together according to some set of business rules without using the database as a temporary storage mechanism.  We would often end up with service classes that would first associate model A to model B, save model A, and then query model B for some further modifications to be performed.  We'd have to reload model B in order to make this query (because it depended on the relationship to A).  And so our services ended up being a sprinkling of save!s and reloads.  And this was not because it was impossible to perform this logic in any other way, but because it was much easier to make the association, save it, and then go on our merry way through the rest of the algorithm.&lt;br /&gt;&lt;br /&gt;And this worked for a little while.  But any sub-section of the service was almost impossible to re-use.  The order of association became very fragile because of our validations - you'd have to disassociate C from A before associating B to A because A couldn't have both B and C.  And perhaps C needed a replacement for A to be valid, so you'd have to find C a new 'A' before you could save C.  And so not only was the service procedural, but it was strictly procedural - you had to perform each step in a defined order.  This made re-use very difficult.  And what we really wanted to do was to move some of the logic onto domain objects, but with all the saving and reloading that was going on, it seemed almost impossible to extract logic that made any sense outside of the context of our complex algorithm.&lt;br /&gt;&lt;br /&gt;And the killer came when we were asked to warn the user before we made this complex change, and give them information about the change that was about to be made so that they could decide whether or not to proceed.  In order to know whether the change was going to be made, and what the change would look like, we'd have to traverse the entire algorithm, by which time the change would have been made.  In short, we couldn't separate the querying code from the modifying code (see Separate Query from Modifier in &lt;a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672"&gt;Refactoring&lt;/a&gt;).  This was the reason we couldn't fulfill the feature, and the reason that we couldn't move cohesive logic to the domain objects.&lt;br /&gt;&lt;br /&gt;It took us two steps to solve these two problems (fragility and Separate Query From Modifier).  I'll describe the solution to the fragility here, and the solution to Separate Query from Modifier in the next post.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Fragility&lt;/h3&gt;&lt;br /&gt;Our validations started to dictate the order in which our objects had to be saved (and therefore the order of the steps in our algorithm), which made re-use of code very difficult.  So we decided that we needed a way to save without triggering the validations, but ensure that only valid objects were left in the database after the algorithm had finished.  ActiveRecord provides a method called save_without_validation that takes care of our first requirement.  And if we called save_without_validation within a transaction, and then raised an Exception if any of the objects were invalid at the end of the algorithm, then the transaction would roll back, and we wouldn't have invalid records in the database.&lt;br /&gt;&lt;br /&gt;So we added a method called save_and_record_without_validation to ActiveRecord::Base which calls save_without_validation on the object, and stores the object in the Thread.current hash so that we can go back at the end of the algorithm and ensure that it is valid. We have a method called validate_recorded_records! which takes a block that contains the calls to save_and_record_without_validation, and then validates after the block has been executed.  The calling code might look something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class='linenum'&gt;    1&lt;/span&gt; &lt;span class="source source_ruby source_ruby_rails"&gt;old_parent &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Parent&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;find_by_name&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;oldie&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='linenum'&gt;    2&lt;/span&gt; new_parent &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Parent&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;find_by_name&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;newbie&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='linenum'&gt;    3&lt;/span&gt; new_child &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Child&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;find&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="constant constant_numeric constant_numeric_ruby"&gt;5&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='linenum'&gt;    4&lt;/span&gt; old_child &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; old_parent&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;child&lt;br /&gt;&lt;span class='linenum'&gt;    5&lt;/span&gt; old_child&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;parent &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; new_parent&lt;br /&gt;&lt;span class='linenum'&gt;    6&lt;/span&gt; old_parent&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;child &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; new_child&lt;br /&gt;&lt;span class='linenum'&gt;    7&lt;/span&gt; &lt;br /&gt;&lt;span class='linenum'&gt;    8&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Parent&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;transaction &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do  &lt;br /&gt;&lt;/span&gt;&lt;span class='linenum'&gt;    9&lt;/span&gt;   validate_recorded_records! &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do    &lt;br /&gt;&lt;/span&gt;&lt;span class='linenum'&gt;   10&lt;/span&gt;     old_child&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;save_and_record_without_validation&lt;br /&gt;&lt;span class='linenum'&gt;   11&lt;/span&gt;     old_parent&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;save_and_record_without_validation  &lt;br /&gt;&lt;span class='linenum'&gt;   12&lt;/span&gt;   &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class='linenum'&gt;   13&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class='linenum'&gt;   14&lt;/span&gt; &lt;br /&gt;&lt;/span&gt;&lt;span class='linenum'&gt;   15&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And if line 10 causes an invalid object until line 11 is executed, then it doesn't matter, because the validation only gets performed after the validate_recorded_records! block has been executed.  The source code for our RecordingHelper can be found &lt;a href="http://www.box.net/shared/nq4c3lao88"&gt;here&lt;/a&gt; and the tests &lt;a href="http://www.box.net/shared/1noqeym80o"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So this solved our fragility problem - the order of execution of the statements no longer mattered as much, which enabled us to extract methods that could be re-used.  But it still doesn't separate query from modifer - we'll tackle that in the next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-6305634278947771311?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/6305634278947771311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=6305634278947771311' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6305634278947771311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6305634278947771311'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2008/01/evolution-of-domain-in-rails-part-1.html' title='The evolution of a Domain in rails: Part 1'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-8190675270925594436</id><published>2007-10-27T08:32:00.000-07:00</published><updated>2007-10-27T08:38:22.929-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='focused unit test'/><category scheme='http://www.blogger.com/atom/ns#' term='textmate'/><category scheme='http://www.blogger.com/atom/ns#' term='module'/><title type='text'>Textmate command for running a focused unit test from a module</title><content type='html'>&lt;a href="http://rickylui.com"&gt;Ricky Lui&lt;/a&gt; and I wrote a textmate command to run a focused unit test from a module.  We sometimes extract common tests to a module and include the module in multiple places.  It's nice to be able to select one unit test from the module file, and have it run the test from each of the files in which the module is included.  Ricky explains it &lt;a href="http://www.rickylui.com/blog/2007/10/09/textmate-bundle-to-run-focused-test-on-a-module/"&gt;here&lt;/a&gt; , and the actual commands can be found &lt;a href="http://www.rickylui.com/blog/2007/10/11/textmate-bundle-to-run-focused-test-on-a-module-part-2/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-8190675270925594436?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/8190675270925594436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=8190675270925594436' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/8190675270925594436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/8190675270925594436'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/10/textmate-command-for-running-focused.html' title='Textmate command for running a focused unit test from a module'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-6363569775914936375</id><published>2007-10-13T12:47:00.000-07:00</published><updated>2009-03-26T12:30:03.231-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMessaging ActiveMQ asynchronous messaging jms jruby rails'/><title type='text'>Testing of jruby JMS vs MRI activemessaging in production mode</title><content type='html'>My previous &lt;a href="http://www.shaneharvie.com/2007/07/performance-of-jruby-jms-messaging.html"&gt;performance tests&lt;/a&gt; of jruby JMS vs MRI activemessaging were flawed - I wasn't running in production mode, and my classes were being reloaded every time a message was processed.  Rookie mistake.  Anyway, here are my latest performance results running in production mode:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_HmLConvEhio/RxEmuBkNxhI/AAAAAAAAABA/Q_Ez81bskUs/s1600-h/performance_production_mode.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_HmLConvEhio/RxEmuBkNxhI/AAAAAAAAABA/Q_Ez81bskUs/s320/performance_production_mode.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5120916823433135634" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It turns out that MRI and activemessaging is faster.  Not sure what causes the strange profile in the MRI test (perhaps garbage collection?), but I ran the test twice and came up with very similar profiles.&lt;br /&gt;&lt;br /&gt;Thanks very much to my friend and colleague &lt;a href="http://joepoon.com/blog"&gt;Joe Poon&lt;/a&gt; for pointing this out to me.  Apologies to anyone who was inconvenienced by this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-6363569775914936375?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/6363569775914936375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=6363569775914936375' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6363569775914936375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6363569775914936375'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/10/testing-of-jruby-jms-vs-mri.html' title='Testing of jruby JMS vs MRI activemessaging in production mode'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_HmLConvEhio/RxEmuBkNxhI/AAAAAAAAABA/Q_Ez81bskUs/s72-c/performance_production_mode.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-3552768936185287971</id><published>2007-08-22T16:52:00.000-07:00</published><updated>2008-12-08T15:44:58.336-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='make constructor protected'/><title type='text'>Making Constructors Protected in Ruby</title><content type='html'>I'm working on Refactoring: Ruby Edition with &lt;a href="http://blog.jayfields.com"&gt;Jay Fields&lt;/a&gt; and &lt;a href="http://martinfowler.com"&gt;Martin Fowler&lt;/a&gt;.  One of the Java examples requires making the constructor protected, which is easy enough to do in Java.  I'd never done it before in Ruby, so I thought I'd give it a shot.  Lets say I have this scenario:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_HmLConvEhio/Rsz_069Qe0I/AAAAAAAAAAk/Nk_stswC7jM/s1600-h/class_diagram.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_HmLConvEhio/Rsz_069Qe0I/AAAAAAAAAAk/Nk_stswC7jM/s320/class_diagram.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5101733762548398914" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I don't want anyone constructing a Party object on its own, so I want to make the constructor protected.  I basically want these tests to pass:&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_new_raises_no_method_error&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; assert_raise&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;NoMethodError&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{&lt;/span&gt;&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;&lt;span class="support support_class support_class_ruby"&gt;Party&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;some name&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_department_initialize&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; d &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Department&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;name&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt; assert_equal &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;name&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; d&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;name&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_employee_initialize&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; e &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Employee&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;name&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;5&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;12&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt; assert_equal &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;name&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; e&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;name&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;I first tried this:&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Party&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;attr_reader&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;&lt;br /&gt; &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;name&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; name&lt;br /&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;protected&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;initialize&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;But the initialize method can happily be called when constructing Party directly, so the first test fails. I then tried this:&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Party&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;attr_reader&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;&lt;br /&gt; &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;name&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; name&lt;br /&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_rails meta_rails_controller"&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;class&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_augmented keyword_operator_assignment_augmented_ruby"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;protected&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;new&lt;/span&gt;&lt;br /&gt; &lt;/span&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Employee&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Party&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; id&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; annual_cost&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;name&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;annual_cost&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; id&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; annual_cost   &lt;br /&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Department&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Party&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;  &lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;which &lt;i&gt;does&lt;/i&gt; prevent me from constructing Party directly.  Unfortunately, I can't construct the concrete classes without getting a NoMethodError because I'm calling the protected method :new, so this isn't much good to me.  But then &lt;a href="http://kotlinski.com"&gt;Andy Kotlinski&lt;/a&gt; showed me that if I implement :new on both of the concrete classes to simply call super:&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Employee&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Party&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; id&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; annual_cost&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;name&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;id&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;annual_cost&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; id&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; annual_cost    &lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;self.new&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; id&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; annual_cost&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Department&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; Party&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;self.new&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;And now all of the tests pass, and my superclass constructor is protected.&lt;br /&gt;&lt;br /&gt;Now, if you don't want to jump through all of those hoops, you can include a module like this:&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_module meta_module_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_module keyword_control_module_ruby"&gt;module&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_module entity_name_type_module_ruby"&gt;MakeConstructorProtected&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;self.included&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;klass&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   klass&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;module_eval &lt;span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block"&gt;do&lt;br /&gt;&lt;/span&gt;&lt;span class="meta meta_rails meta_rails_controller"&gt;      &lt;span class="keyword keyword_control keyword_control_ruby"&gt;class&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_augmented keyword_operator_assignment_augmented_ruby"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt; &lt;br /&gt;       &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;protected&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;new&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;       &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;inherited&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;klass&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;         klass&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;module_eval &lt;span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block"&gt;do&lt;br /&gt;&lt;/span&gt;            &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;self.new&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;&lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;*&lt;/span&gt;args&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby"&gt;super&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_statement punctuation_separator_statement_ruby"&gt;;&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;         &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;       &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;     &lt;/span&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Party&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;include&lt;/span&gt; &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;MakeConstructorProtected&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;attr_reader&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;name&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;name&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; name&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;And you don't need to modify any of your subclasses. Because the inclusion of the module documents the intention far better than the obscure code, I quite like this solution.  It still feels a little un-ruby-like, but it does document that Party wasn't written with the intention of being constructed on its own, and I quite like that.  If anyone has an easier way to do this, then I'd love to hear it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-3552768936185287971?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/3552768936185287971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=3552768936185287971' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/3552768936185287971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/3552768936185287971'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/08/making-constructors-protected-in-ruby.html' title='Making Constructors Protected in Ruby'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_HmLConvEhio/Rsz_069Qe0I/AAAAAAAAAAk/Nk_stswC7jM/s72-c/class_diagram.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-5207941897696839357</id><published>2007-07-26T16:31:00.000-07:00</published><updated>2008-08-22T11:02:37.459-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='moist tests'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='DRY'/><title type='text'>Moist Tests: Production Code Needs to be DRY, But Tests Don't</title><content type='html'>I've spoken a bit about this before when discussing &lt;a href="http://beechbonanza.blogspot.com/2007/05/new-notation-for-standardparams.html"&gt;our standard_params notation&lt;/a&gt;. I think we've taken the &lt;a href="http://en.wikipedia.org/wiki/DRY"&gt;DRY&lt;/a&gt; principle too far.  And I think this happens a lot in the software industry.  We latch onto a good principle, and our meticulous (some would say anal) natures lead us to want to apply it everywhere.  And we do it with DRY.  Don't Repeat Yourself. Removing duplication in code removes bugs.  Re-use promotes efficient development. It's a fantastic principle, first outlined by Dave Thomas and Andy Hunt in The Pragmatic Programmer.  It's probably still underused in most code bases.  But one area where I see it abused is in tests.  Tests don't need to by DRY, they can be Moist*.  Take these two tests for example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_formatted_telephone_number&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  telephone_number &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;TelephoneNumber&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;area_code&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;123&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;telephone_number_base&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;5556811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  assert_equal &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;(123) 555-6811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; telephone_number&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;formatted&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_unformatted_telephone_number&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  telephone_number &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;TelephoneNumber&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;area_code&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;123&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;telephone_number_base&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;5556811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;  assert_equal &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;1235556811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; telephone_number&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;unformatted  &lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;DRY would see the duplicate creation of the same TelephoneNumber object, and say "Extract Method" to remove the duplication. Or perhaps we could stick an instance variable in our setup, and assign the TelephoneNumber object to it there:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;setup&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;telephone_number&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;TelephoneNumber&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;area_code&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;123&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;telephone_number_base&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;5556811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;test_unformatted_telephone_number&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  assert_equal &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;1235556811&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;telephone_number&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;unformatted  &lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And if this wasn't a test (if it was actual code that will be executed in production), I would definitely agree.  If you remove the duplication, you only have one place to change the code when that time arises, and bugs are less likely to result.  But in tests it is different.  When a test fails, I want to fix it quickly.  And when I have to search around a large test class for extracted methods, or instance variables that may be modified throughout the class, this costs me time.  And I'm willing to accept the risk of the duplication causing a bug (after all, it is only test code) if I reduce the time wasted.&lt;br /&gt;&lt;br /&gt;So for me, private methods in test are a smell.  So too are setup methods.  The only time I use them is when I can get the abstraction such that I very very rarely have to look at what is happening beneath the abstraction.  A good example is rails' Controller tests.  They have a setup which initializes the controller, and a request and response object.  I think I can count on one hand the number of times that I've had to care about these variables, so in that case, it's ok.&lt;br /&gt;&lt;br /&gt;*I believe &lt;a href="http://www.somethingnimble.com/collaborators/zak"&gt;zak&lt;/a&gt; came up with this term, though I'm not sure.  It may have been &lt;a href="http://blog.jayfields.com/"&gt;Jay Fields&lt;/a&gt;.  Either way, I'm certainly not taking credit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-5207941897696839357?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/5207941897696839357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=5207941897696839357' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/5207941897696839357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/5207941897696839357'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/07/production-code-needs-to-be-dry-tests.html' title='Moist Tests: Production Code Needs to be DRY, But Tests Don&apos;t'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-6685789891568502491</id><published>2007-07-05T17:13:00.000-07:00</published><updated>2008-12-08T15:44:58.442-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMessaging'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='jms'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous messaging'/><title type='text'>Performance of the JRuby JMS messaging solution</title><content type='html'>Please note: This performance testing method was flawed as it was undertaken in the 'development' environment.  There are more accurate results &lt;a href="http://www.shaneharvie.com/2007/10/testing-of-jruby-jms-vs-mri.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now that we have a &lt;a href="http://beechbonanza.blogspot.com/2007/07/jruby-jms-as-replacement-for.html"&gt;JRuby JMS&lt;/a&gt; implementation, how does it perform? I did a quick test to compare the &lt;a href="http://beechbonanza.blogspot.com/2007/06/asynchronous-messaging-with-rails.html"&gt;ActiveMessaging solution&lt;/a&gt; with the JRuby JMS messaging solution. The Customer application, Orders application, ActiveMQ server and the message handler (ActiveMessaging poller/JRuby JMS process) were all running on my laptop.  As such, the absolute timings presented here are not relevant, but the comparison between the two solutions is relevant, and somewhat interesting.  &lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;h4&gt;Testing Method&lt;/h4&gt;&lt;/span&gt;&lt;br /&gt;I sent 100 messages from the Customer application and measured the elapsed time between when the message was sent, and when processing had finished in the Orders application. With each solution, I captured the current time immediately before the message was sent in the Customer application, and captured the current time immediately after it was processed in the Orders application.  These were the results:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_HmLConvEhio/Ro2M1lov-kI/AAAAAAAAAAU/CuhS38-nQOw/s1600-h/performance_comparison.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_HmLConvEhio/Ro2M1lov-kI/AAAAAAAAAAU/CuhS38-nQOw/s320/performance_comparison.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5083874406635141698" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the elapsed time increases at a constant rate for the ActiveMessaging solution, as the 100 messages pile up in the queue.  The last message took just under 14 seconds to be processed (with a significant amount of that time being spent in the queue).  Contrast this with the Java JMS solution, where the 100th message took only 2.5 seconds to be processed.  Even more interesting than the difference between the two times is the flattening of the Java JMS curve.  The elapsed time does start to grow as the messages pile up in the queue, but after around the 60th message, the elapsed time remains relatively constant.  This represents a great advantage when trying to ensure predictable performance of your messaging solution.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;br /&gt;We are going to look seriously at using JRuby and JMS on our current project in place of ActiveMessaging.  I mentioned in my last post that although I deployed the Orders application using JRuby, you can actually leave the web-app itself in MRI  (Matz Ruby Implementation) and just deploy the message handler in JRuby.  The message handler is running as a separate process to the web application.  It needs to load the rails application to have access to the Customer model so that it can save the message contents to the database, but that's it. The web requests can still be handled by an MRI-deployed rails app.  So that's probably what we will end up doing.  Thoughtworks Studios recently released a project management tool called &lt;a href="http://studios.thoughtworks.com/mingle-project-intelligence"&gt;Mingle&lt;/a&gt; that is deployed on JRuby, so it's definitely possible to deploy the whole web-app in JRuby, but as an interim step we will probably stick with MRI.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-6685789891568502491?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/6685789891568502491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=6685789891568502491' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6685789891568502491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6685789891568502491'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/07/performance-of-jruby-jms-messaging.html' title='Performance of the JRuby JMS messaging solution'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_HmLConvEhio/Ro2M1lov-kI/AAAAAAAAAAU/CuhS38-nQOw/s72-c/performance_comparison.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-1567330413572174707</id><published>2007-07-05T16:56:00.000-07:00</published><updated>2007-07-05T17:41:27.002-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='jms'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><title type='text'>JRuby JMS as a replacement for ActiveMessaging</title><content type='html'>I was talking to JRuby committer &lt;a href="http://ola-bini.blogspot.com/"&gt;Ola Bini&lt;/a&gt; at RailsConf a few weeks ago about our ActiveMessaging solution, and he suggested we try using &lt;a href="http://en.wikipedia.org/wiki/JRuby"&gt;JRuby&lt;/a&gt; and &lt;a href="http://java.sun.com/products/jms/"&gt;JMS&lt;/a&gt; instead. By using JMS, we can eliminate the poller and have a truly "Event Driven" solution.  So I've been playing around with JRuby and JMS in my spare time, with some really interesting results.  In JRuby, we can implement the Java JMS interface MessageListener:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_require meta_require_ruby"&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;require&lt;/span&gt; &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;java&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;include_class &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;org.apache.activemq.ActiveMQConnectionFactory&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;include_class &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;org.apache.activemq.util.ByteSequence&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;include_class &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;org.apache.activemq.command.ActiveMQBytesMessage&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;include_class &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;javax.jms.MessageListener&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_environment-variable meta_environment-variable_ruby"&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;ENV&lt;/span&gt;[&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;RAILS_ENV&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;]&lt;/span&gt; = &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;development&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RAILS_ROOT&lt;/span&gt;=&lt;span class="support support_class support_class_ruby"&gt;File&lt;/span&gt;.expand_path(&lt;span class="support support_class support_class_ruby"&gt;File&lt;/span&gt;.join(&lt;span class="support support_class support_class_ruby"&gt;File&lt;/span&gt;.dirname(&lt;span class="variable variable_language variable_language_ruby"&gt;__FILE__&lt;/span&gt;), &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;..&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;,&lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;..&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;))&lt;br /&gt;load &lt;span class="support support_class support_class_ruby"&gt;File&lt;/span&gt;.join(&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;config&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;environment.rb&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;MessageHandler&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;include&lt;/span&gt; javax.jms.&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;MessageListener&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;onMessage&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;serialized_message&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    message_body = serialized_messageed_message.get_content.get_data.inject(&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;) {&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;|body, byte| body &amp;lt;&amp;lt; byte }&lt;br /&gt;    customer_payload = &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;YAML&lt;/span&gt;.load(message_body)&lt;br /&gt;    customer = &lt;span class="support support_class support_class_ruby"&gt;Customer&lt;/span&gt;.&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;(&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt; =&amp;gt; customer_payload.name)&lt;br /&gt;    customer.id = customer_payload.id&lt;br /&gt;    customer.save!&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_without-arguments meta_function_method_without-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;run&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    factory = &lt;span class="support support_class support_class_ruby"&gt;ActiveMQConnectionFactory&lt;/span&gt;.&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;(&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;tcp://localhost:61616&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;    connection = factory.create_connection();&lt;br /&gt;    session = connection.create_session(&lt;span class="constant constant_language constant_language_ruby"&gt;false&lt;/span&gt;, &lt;span class="support support_class support_class_ruby"&gt;Session&lt;/span&gt;::&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;AUTO_ACKNOWLEDGE&lt;/span&gt;);&lt;br /&gt;    queue = session.create_queue(&lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;Customer&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;);&lt;br /&gt;    &lt;br /&gt;    consumer = session.create_consumer(queue);&lt;br /&gt;    consumer.set_message_listener(&lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;);&lt;br /&gt;    &lt;br /&gt;    connection.start();&lt;br /&gt;    puts &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;Listening...&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;handler = &lt;span class="support support_class support_class_ruby"&gt;MessageHandler&lt;/span&gt;.&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;br /&gt;handler.run&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Contrast this with similar Java code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_java"&gt;&lt;span class="meta meta_import meta_import_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;import&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_import entity_name_type_import_java"&gt;org.apache.activemq.ActiveMQConnectionFactory&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class="meta meta_import meta_import_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;import&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_import entity_name_type_import_java"&gt;org.apache.activemq.util.ByteSequence&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class="meta meta_import meta_import_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;import&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_import entity_name_type_import_java"&gt;org.apache.activemq.command.ActiveMQBytesMessage&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_import meta_import_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;import&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_import entity_name_type_import_java"&gt;javax.jms.*&lt;/span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_definition meta_definition_class meta_definition_class_java"&gt;&lt;span class="storage storage_modifier storage_modifier_java"&gt;public &lt;/span&gt;&lt;span class="storage storage_type storage_type_java"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_java"&gt;MessageHandler&lt;/span&gt; &lt;span class="meta meta_definition meta_definition_class meta_definition_class_implements meta_definition_class_implements_java"&gt;&lt;span class="storage storage_modifier storage_modifier_java"&gt;implements&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;MessageListener&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;{&lt;br /&gt;&lt;br /&gt;    &lt;span class="storage storage_modifier storage_modifier_access-control storage_modifier_access-control_java"&gt;private&lt;/span&gt; &lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;Connection&lt;/span&gt; connection;&lt;br /&gt;    &lt;span class="storage storage_modifier storage_modifier_access-control storage_modifier_access-control_java"&gt;private&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;Session&lt;/span&gt; session;&lt;br /&gt;    &lt;span class="storage storage_modifier storage_modifier_access-control storage_modifier_access-control_java"&gt;private&lt;/span&gt; &lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;Queue&lt;/span&gt; queue;&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_definition meta_definition_method meta_definition_method_java"&gt;    &lt;span class="storage storage_modifier storage_modifier_java"&gt;public &lt;/span&gt;&lt;span class="storage storage_type storage_type_java"&gt;void&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_java"&gt;onMessage&lt;/span&gt;&lt;span class="meta meta_definition meta_definition_param-list meta_definition_param-list_java"&gt;(&lt;span class="storage storage_type storage_type_java"&gt;Message&lt;/span&gt; serializedMessage&lt;/span&gt;) &lt;/span&gt;{&lt;br /&gt;        &lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;String&lt;/span&gt; message = &lt;span class="string string_quoted string_quoted_double string_quoted_double_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_java"&gt;"&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_java"&gt;"&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;        &lt;span class="storage storage_type storage_type_java"&gt;ActiveMQBytesMessage&lt;/span&gt; bytes_message = (&lt;span class="storage storage_type storage_type_java"&gt;ActiveMQBytesMessage&lt;/span&gt;)serializedMessage;&lt;br /&gt;        &lt;span class="storage storage_type storage_type_java"&gt;ByteSequence&lt;/span&gt; sequence = bytes_message.getContent();&lt;br /&gt;&lt;br /&gt;        &lt;span class="keyword keyword_control keyword_control_java"&gt;for&lt;/span&gt;(&lt;span class="storage storage_type storage_type_java"&gt;int&lt;/span&gt; i=&lt;span class="constant constant_numeric constant_numeric_java"&gt;0&lt;/span&gt;; i &lt;span class="keyword keyword_operator keyword_operator_comparison keyword_operator_comparison_java"&gt;&amp;lt;&lt;/span&gt; sequence.getData().length; i&lt;span class="keyword keyword_operator keyword_operator_increment-decrement keyword_operator_increment-decrement_java"&gt;++&lt;/span&gt;) {&lt;br /&gt;            message &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_java"&gt;+&lt;/span&gt;= (&lt;span class="storage storage_type storage_type_java"&gt;char&lt;/span&gt;)sequence.getData()[i];&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_java"&gt;//&lt;/span&gt; Here is where you would use the message to create the Customer object&lt;br /&gt;&lt;/span&gt;        &lt;span class="comment comment_line comment_line_double-slash comment_line_double-slash_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_java"&gt;//&lt;/span&gt; For now I will just print the YAML&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;System&lt;/span&gt;.out.println(message);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_definition meta_definition_method meta_definition_method_java"&gt;    &lt;span class="storage storage_modifier storage_modifier_java"&gt;public static &lt;/span&gt;&lt;span class="storage storage_type storage_type_java"&gt;void&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_java"&gt;main&lt;/span&gt;&lt;span class="meta meta_definition meta_definition_param-list meta_definition_param-list_java"&gt;(&lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;String&lt;/span&gt;[] args&lt;/span&gt;) &lt;span class="meta meta_definition meta_definition_throws meta_definition_throws_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;throws&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;JMSException&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;{&lt;br /&gt;        &lt;span class="storage storage_type storage_type_java"&gt;MessageHandler&lt;/span&gt; handler = &lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;new&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;MessageHandler&lt;/span&gt;();&lt;br /&gt;        handler.run();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;span class="meta meta_definition meta_definition_method meta_definition_method_java"&gt;    &lt;span class="storage storage_modifier storage_modifier_java"&gt;private &lt;/span&gt;&lt;span class="storage storage_type storage_type_java"&gt;void&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_java"&gt;run&lt;/span&gt;&lt;span class="meta meta_definition meta_definition_param-list meta_definition_param-list_java"&gt;(&lt;/span&gt;) &lt;span class="meta meta_definition meta_definition_throws meta_definition_throws_java"&gt;&lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;throws&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;JMSException&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;{&lt;br /&gt;        &lt;span class="storage storage_type storage_type_java"&gt;ActiveMQConnectionFactory&lt;/span&gt; factory = &lt;span class="keyword keyword_other keyword_other_class-fns keyword_other_class-fns_java"&gt;new&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;ActiveMQConnectionFactory&lt;/span&gt;(&lt;span class="string string_quoted string_quoted_double string_quoted_double_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_java"&gt;"&lt;/span&gt;tcp://localhost:61616&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_java"&gt;"&lt;/span&gt;&lt;/span&gt;);&lt;br /&gt;        connection = factory.createConnection();&lt;br /&gt;        session = connection.createSession(&lt;span class="constant constant_language constant_language_java"&gt;false&lt;/span&gt;, &lt;span class="storage storage_type storage_type_java"&gt;Session&lt;/span&gt;.&lt;span class="constant constant_other constant_other_java"&gt;AUTO_ACKNOWLEDGE&lt;/span&gt;);&lt;br /&gt;        queue = session.createQueue(&lt;span class="string string_quoted string_quoted_double string_quoted_double_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_java"&gt;"&lt;/span&gt;Customer&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_java"&gt;"&lt;/span&gt;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;        &lt;span class="storage storage_type storage_type_java"&gt;MessageConsumer&lt;/span&gt; consumer = session.createConsumer(queue);&lt;br /&gt;        consumer.setMessageListener(&lt;span class="variable variable_language variable_language_java"&gt;this&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;        connection.start();&lt;br /&gt;        &lt;span class="support support_type support_type_built-ins support_type_built-ins_java"&gt;System&lt;/span&gt;.out.println(&lt;span class="string string_quoted string_quoted_double string_quoted_double_java"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_java"&gt;"&lt;/span&gt;Listening...&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_java"&gt;"&lt;/span&gt;&lt;/span&gt;);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You'll notice that the 'run' methods are identical, save for the "rubyisation" of the Java code (underscores and the like).  Java requires you to implement the onMessage method when inheriting from the MessageListener class, so we have done that in JRuby.  &lt;br /&gt;&lt;br /&gt;The Java class definition:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_java"&gt;&lt;span class="meta meta_definition meta_definition_class meta_definition_class_java"&gt;&lt;span class="storage storage_modifier storage_modifier_java"&gt;public &lt;/span&gt;&lt;span class="storage storage_type storage_type_java"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_java"&gt;MessageHandler&lt;/span&gt; &lt;span class="meta meta_definition meta_definition_class meta_definition_class_implements meta_definition_class_implements_java"&gt;&lt;span class="storage storage_modifier storage_modifier_java"&gt;implements&lt;/span&gt; &lt;span class="storage storage_type storage_type_java"&gt;MessageListener&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;is replaced with the JRuby equivalent:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;MessageHandler&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;include&lt;/span&gt; javax.jms.&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;MessageListener&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The rest of the JRuby code is very similar to its Java equivalent, though it was tough going back to the type-safety gymnastics of Java, I must admit.&lt;br /&gt;&lt;br /&gt;I was surprised that with the version of JRuby I am running (ruby 1.8.5 (2007-06-02 rev 3812) [i386-jruby1.0.0RC3]), I had to use camel case for the onMessage method definition rather then ruby-like underscores, but it's no big deal.&lt;br /&gt;&lt;br /&gt;We'll save this file under the processors directory of our &lt;a href="http://beechbonanza.blogspot.com/2007/06/asynchronous-messaging-with-rails.html"&gt;Orders&lt;/a&gt; application as message_handler.rb&lt;br /&gt;&lt;br /&gt;Then we can deploy the Orders application in JRuby.  Note that this isn't strictly necessary (but more on that later).&lt;br /&gt;&lt;br /&gt;From the instructions on the &lt;a href="http://www.headius.com/jrubywiki/index.php/Rails_Integration"&gt;JRuby wiki&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;Install the Goldspike plugin:&lt;br /&gt;~/orders]&gt;script/plugin install svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration/plugins/goldspike&lt;br /&gt;&lt;br /&gt;Install the ActiveRecord-JDBC gem:&lt;br /&gt;~/orders]&gt;gem install activerecord-jdbc --no-rdoc --no-ri&lt;br /&gt;&lt;br /&gt;The plugin provides the following rake task:&lt;br /&gt;war:standalone:create - which packages up a web archive containing your application along with JRuby and the rails libraries&lt;br /&gt;&lt;br /&gt;Run the task, and deploy the war to your app server (I'm using Tomcat)&lt;br /&gt;&lt;br /&gt;We can then make the apache-activemq-4.1.1.jar available to our script for the jms-related code by setting the CLASSPATH environment variable:&lt;br /&gt;&lt;br /&gt;export CLASSPATH=/path_to_jar/apache-activemqvemq-4.1.1.jar:$CLASSPATH&lt;br /&gt;&lt;br /&gt;Then all we need to do is run the JRuby code with the following command:&lt;br /&gt;&lt;br /&gt;jruby /path_to_tomcat/webapps/orders/processors/message_handler.rb&lt;br /&gt;&lt;br /&gt;And that's it.  Now we can process Customer messages using our JRuby JMS implementation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-1567330413572174707?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/1567330413572174707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=1567330413572174707' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/1567330413572174707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/1567330413572174707'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/07/jruby-jms-as-replacement-for.html' title='JRuby JMS as a replacement for ActiveMessaging'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-1703226805767362166</id><published>2007-06-25T12:35:00.001-07:00</published><updated>2007-07-05T17:12:40.000-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMessaging'/><category scheme='http://www.blogger.com/atom/ns#' term='reliable'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><title type='text'>Asynchronous Messaging with Rails</title><content type='html'>When asked how to integrate with other applications, the vast majority of Rails developers would answer "&lt;a href="http://en.wikipedia.org/wiki/REST"&gt;REST&lt;/a&gt;".  And when they answer REST, they almost always mean synchronously.  Now I have nothing against REST per se, but I will always favour asynchronous communication over synchronous.  An asynchronous messaging solution such as JMS has well-documented advantages such as:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The message producer can "fire and forget", sending the message and then moving on to more important work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The mechanisms required for reliability are relatively simple in the producer and consumer, and the more difficult tasks (such as persistence) can be handled in the message broker.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It is easy to add redundancy into the messaging infrastructure&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;For further reference, a good book on the topic is Gregor Hohpe's &lt;a href="http://www.amazon.com/Enterprise-Integration-Patterns-Designing-Addison-Wesley/dp/0321200683"&gt;Enterprise Integration Patterns&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;On a number of occasions I've started out with synchronous messaging, only to say to myself "I really want this to be reliable".  So I fire off an http request and wait for a 200.  If I don't receive a 200 after a certain amount of time, then I try again.  I'll repeat this for a few times, then I'll give up, and log the failure somewhere.&lt;br /&gt;&lt;br /&gt;Then I say to myself "It would be really nice to have this re-trying out-of-process".  So I add another process that receives a request for a message to be sent, sends the message, and performs any re-trying that is required. And all of a sudden, I have asynchronous messaging. Now, there's nothing about REST that prevents asynchronous messaging, but I rarely see anyone from the Rails community talking about anything but synchronous messaging.  So I thought I'd talk about how we've solved the problem on our project.  We've used the &lt;a href="http://code.google.com/p/activemessaging/wiki/ActiveMessaging"&gt;ActiveMessaging&lt;/a&gt; plugin, originally written by some people from Thoughtworks.  I'll demonstrate ActiveMessaging here, and then over the next week or two I'll contrast it with a JMS JRuby solution I've been playing around with.&lt;br /&gt;&lt;br /&gt;Let's say we have two Rails applications - a Customer Management application and an Order Management application.  The Customer application is responsible for managing all things to do with Customers - their personal details, any communications between our business and the customers, and the customer's user preferences. The Customer Management app has a rich view of a customer with fields such as:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Name&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Address&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Telephone Number, etc.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;The Order Management application needs to associate Orders with Customers, so that even when the Customer application is down for scheduled maintenance, orders can still be taken.  The Orders app has a simple view of a Customer, with just a name and an id.&lt;br /&gt;&lt;br /&gt;We will make the Customer application responsible for creating, updating and deleting Customers.  Whenever any of these actions are performed, the Customer application notifies the Orders application with a message. The message indicates the type of action performed (create, update, or delete), and the id of the customer, so that the two 'views' of the Customer object (in the two different applications) can be kept in sync. The Orders application then creates, updates, or deletes its view of the customer according to the type of message that is sent. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;First, we will need to setup an ActiveMQ Server.  I've set it up locally on my machine using these instructions (I used the Linux instructions for my Macbook Pro):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://activemq.apache.org/getting-started.html"&gt;http://activemq.apache.org/getting-started.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In both of our Rails applications we will need to install the ActiveMessaging plugin:&lt;br /&gt;&lt;br /&gt;script/plugin install http://activemessaging.googlecode.com/svn/trunk/plugins/activemessaging&lt;br /&gt;&lt;br /&gt;ActiveMessaging provides the ability to send and receive messages.  Our Customer application will send messages, and our Orders application will receive them.&lt;br /&gt;&lt;br /&gt;For the purposes of this discussion, I will only show the 'create' messages, though the process would be very similar for 'update' and 'delete'.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Customer application&lt;/h4&gt;&lt;br /&gt;Our Customer application has a Customer object that inherits from ActiveRecord::Base:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_rails meta_rails_model"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;Customer&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Base&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It has the following fields:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;string&lt;/span&gt;&lt;br /&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;address&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;string&lt;/span&gt;&lt;br /&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;telephone_number&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;string&lt;/span&gt;&lt;br /&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;created_at&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;date_time&lt;/span&gt;&lt;br /&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;updated_at&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;date_time&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our message will be a YAML serialized message of the following simple data object:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;CustomerPayload&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;attr_accessor&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;id&lt;/span&gt;, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;params&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;id&lt;/span&gt; = params[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;id&lt;/span&gt;]&lt;br /&gt;    &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;name&lt;/span&gt; = params[&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt;]&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We use a rails observer to observe the 'create' event of the Customer object, construct a CustomerPayload object, serialize it, and send it via ActiveMessaging:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="meta meta_require meta_require_ruby"&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;require&lt;/span&gt; &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;activemessaging/processor&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;CustomerObserver&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; ActiveRecord::Observer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br /&gt;  &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;include&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;ActiveMessaging&lt;/span&gt;::&lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;MessageSender&lt;/span&gt;&lt;br /&gt;  observe &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;Customer&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  publishes_to &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;customer_queue&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;after_create&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;customer&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    payload = &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;YAML&lt;/span&gt;.dump(&lt;span class="support support_class support_class_ruby"&gt;CustomerPayload&lt;/span&gt;.&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;(&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;id&lt;/span&gt; =&amp;gt; customer.id, &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt; =&amp;gt; customer.name))&lt;br /&gt;    publish &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;customer_queue&lt;/span&gt;, payload&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To configure connection to the ActiveMQ server, I'll need the following configuration files in both of my rails applications:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;config/broker.yml&lt;br /&gt;&lt;/span&gt;development:&lt;br /&gt;    adapter: stomp&lt;br /&gt;    login: &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    passcode: &lt;span class="string string_quoted string_quoted_double string_quoted_double_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;"&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    host: localhost&lt;br /&gt;    port: &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;61613&lt;/span&gt;&lt;br /&gt;    reliable: &lt;span class="constant constant_language constant_language_ruby"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;config/messaging.rb&lt;br /&gt;&lt;/span&gt;&lt;span class="support support_class support_class_ruby"&gt;ActiveMessaging&lt;/span&gt;::&lt;span class="support support_class support_class_ruby"&gt;Gateway&lt;/span&gt;.define &lt;span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block"&gt;do &lt;/span&gt;|s|  &lt;br /&gt;  s.queue &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;customer_queue&lt;/span&gt;, &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;/queue/Customer&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And to get the observer to work, I'll need this line in environment.rb  &lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt; Activate observers that should always be running&lt;br /&gt;&lt;/span&gt;config.active_record.observers = &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;customer_observer&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Orders application&lt;/h4&gt;&lt;br /&gt;For consuming messages, ActiveMessaging provides a directory under app called 'processors'.  In this directory of my Orders application I place my CustomerMessageProcessor:&lt;br /&gt;&lt;br /&gt;&lt;pre class="textmate-source espresso_libre"&gt;&lt;span class="source source_ruby source_ruby_rails"&gt;&lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;customer_message_processor.rb&lt;br /&gt;&lt;/span&gt;&lt;span class="meta meta_require meta_require_ruby"&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;require&lt;/span&gt; &lt;span class="string string_quoted string_quoted_single string_quoted_single_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby"&gt;'&lt;/span&gt;processors/application&lt;span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="meta meta_class meta_class_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_class keyword_control_class_ruby"&gt;class&lt;/span&gt; &lt;span class="entity entity_name entity_name_type entity_name_type_class entity_name_type_class_ruby"&gt;CustomerMessageProcessor&lt;span class="entity entity_other entity_other_inherited-class entity_other_inherited-class_ruby"&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_inheritance punctuation_separator_inheritance_ruby"&gt;&amp;lt;&lt;/span&gt; ApplicationProcessor&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  subscribes_to &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;customer_queue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;on_message&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt;serialized_message&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    customer_payload = &lt;span class="variable variable_other variable_other_constant variable_other_constant_ruby"&gt;YAML&lt;/span&gt;.load(serialized_message)&lt;br /&gt;    customer = &lt;span class="support support_class support_class_ruby"&gt;Customer&lt;/span&gt;.&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;(&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;name&lt;/span&gt; =&amp;gt; customer_payload.name)&lt;br /&gt;    customer.id = customer_payload.id&lt;br /&gt;    customer.save!&lt;br /&gt;  &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This class will need access to the same CustomerPayload object as the Customers application for deserialization.&lt;br /&gt;&lt;br /&gt;In ActiveMessaging, a poller is used to poll the queue and trigger the on_message method shown above.  We'll need to start the poller in the Orders application using the following command:&lt;br /&gt;&lt;br /&gt;script/poller run&lt;br /&gt;&lt;br /&gt;And that's it.  If both the Orders and Customer applications are running, and have access to the ActiveMQ server, then when we create a Customer in the Customer application, it should show up in the Orders application a few moments later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-1703226805767362166?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/1703226805767362166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=1703226805767362166' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/1703226805767362166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/1703226805767362166'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/06/asynchronous-messaging-with-rails.html' title='Asynchronous Messaging with Rails'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-185582574694183476</id><published>2007-06-25T12:35:00.000-07:00</published><updated>2007-07-05T17:11:27.436-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMessaging'/><category scheme='http://www.blogger.com/atom/ns#' term='reliable'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='asynchronous'/><title type='text'>Asynchronous Messaging with Rails</title><content type='html'>When asked how to integrate with other applications, the vast majority of Rails developers would answer "&lt;a href="http://en.wikipedia.org/wiki/REST"&gt;REST&lt;/a&gt;".  And when they answer REST, they almost always mean synchronously.  Now I have nothing against REST per se, but I will always favour asynchronous communication over synchronous.  An asynchronous messaging solution such as JMS has well-documented advantages such as:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The message producer can "fire and forget", sending the message and then moving on to more important work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The mechanisms required for reliability are relatively simple in the producer and consumer, and the more difficult tasks (such as persistence) can be handled in the message broker.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It is easy to add redundancy into the messaging infrastructure&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;For further reference, a good book on the topic is Gregor Hohpe's &lt;a href="http://www.amazon.com/Enterprise-Integration-Patterns-Designing-Addison-Wesley/dp/0321200683"&gt;Enterprise Integration Patterns&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;On a number of occasions I've started out with synchronous messaging, only to say to myself "I really want this to be reliable".  So I fire off an http request and wait for a 200.  If I don't receive a 200 after a certain amount of time, then I try again.  I'll repeat this for a few times, then I'll give up, and log the failure somewhere.&lt;br /&gt;&lt;br /&gt;Then I say to myself "It would be really nice to have this re-trying out-of-process".  So I add another process that receives a request for a message to be sent, sends the message, and performs any re-trying that is required. And all of a sudden, I have asynchronous messaging. Now, there's nothing about REST that prevents asynchronous messaging, but I rarely see anyone from the Rails community talking about anything but synchronous messaging.  So I thought I'd talk about how we've solved the problem on our project.  We've used the &lt;a href="http://code.google.com/p/activemessaging/wiki/ActiveMessaging"&gt;ActiveMessaging&lt;/a&gt; plugin, originally written by some people from Thoughtworks.  I'll demonstrate ActiveMessaging here, and then over the next week or two I'll contrast it with a JMS JRuby solution I've been playing around with.&lt;br /&gt;&lt;br /&gt;Let's say we have two Rails applications - a Customer Management application and an Order Management application.  The Customer application is responsible for managing all things to do with Customers - their personal details, any communications between our business and the customers, and the customer's user preferences. The Customer Management app has a rich view of a customer with fields such as:&lt;br /&gt;&lt;br /&gt;Name&lt;br /&gt;Address&lt;br /&gt;Phone Number&lt;br /&gt;Social Security Number&lt;br /&gt;etc.&lt;br /&gt;&lt;br /&gt;The Order Management application needs to associate Orders with Customers, so that even when the Customer application is down for scheduled maintenance, orders can still be taken.  The Orders app has a simple view of a Customer, with just a name and an id.&lt;br /&gt;&lt;br /&gt;We will make the Customer application responsible for creating, updating and deleting Customers.  Whenever any of these actions are performed, the Customer application notifies the Orders application with a message. The message indicates the type of action performed (create, update, or delete), and the id of the customer, so that the two 'views' of the Customer object (in the two different applications) can be kept in sync. The Orders application then creates, updates, or deletes its view of the customer according to the type of message that is sent. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;First, we will need to setup an ActiveMQ Server.  I've set it up locally on my machine using these instructions (I used the Linux instructions for my Macbook Pro):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://activemq.apache.org/getting-started.html"&gt;http://activemq.apache.org/getting-started.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In both of our Rails applications we will need to install the ActiveMessaging plugin:&lt;br /&gt;&lt;br /&gt;script/plugin install http://activemessaging.googlecode.com/svn/trunk/plugins/activemessaging&lt;br /&gt;&lt;br /&gt;ActiveMessaging provides the ability to send and receive messages.  Our Customer application will send messages, and our Orders application will receive them.&lt;br /&gt;&lt;br /&gt;For the purposes of this discussion, I will only show the 'create' messages, though the process would be very similar for 'update' and 'delete'.&lt;br /&gt;&lt;br /&gt;Customer application&lt;br /&gt;Our Customer application has a Customer object that inherits from ActiveRecord::Base:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;class Customer &lt; ActiveRecord::Base&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It has the following fields:&lt;br /&gt;&lt;br /&gt;:name, :string&lt;br /&gt;:address, :string&lt;br /&gt;:telephone_number, :string&lt;br /&gt;:created_at, :date_time&lt;br /&gt;:updated_at, :date_time&lt;br /&gt;&lt;br /&gt;Our message will be a YAML serialized message of the following simple data object:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;class CustomerPayload&lt;br /&gt;  attr_accessor :id, :name&lt;br /&gt;  &lt;br /&gt;  def initialize(params)&lt;br /&gt;    @id = params[:id]&lt;br /&gt;    @name = params[:name]&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We use a rails observer to observe the 'create' event of the Customer object, construct a CustomerPayload object, serialize it, and send it via ActiveMessaging:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;require 'activemessaging/processor'&lt;br /&gt;class CustomerObserver &lt; ActiveRecord::Observer  &lt;br /&gt;  include ActiveMessaging::MessageSender&lt;br /&gt;  observe Customer&lt;br /&gt;  &lt;br /&gt;  publishes_to :customer_queue&lt;br /&gt;  &lt;br /&gt;  def after_create(customer)&lt;br /&gt;    payload = YAML.dump(CustomerPayload.new(:id =&gt; customer.id, :name =&gt; customer.name))&lt;br /&gt;    publish :customer_queue, payload&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To configure connection to the ActiveMQ server, I'll need the following configuration files in both of my rails applications:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;#config/broker.yml&lt;br /&gt;development:&lt;br /&gt;    adapter: stomp&lt;br /&gt;    login: ""&lt;br /&gt;    passcode: ""&lt;br /&gt;    host: localhost&lt;br /&gt;    port: 61613&lt;br /&gt;    reliable: false&lt;br /&gt;&lt;br /&gt;#config/messaging.rb&lt;br /&gt;ActiveMessaging::Gateway.define do |s|  &lt;br /&gt;  s.queue :customer_queue, '/queue/Customer'&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And to get the observer to work, I'll need this line in environment.rb  &lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;# Activate observers that should always be running&lt;br /&gt;config.active_record.observers = :customer_observer&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For consuming messages, ActiveMessaging provides a directory under app call 'processors'.  In this directory of my Orders application I place my CustomerMessageProcessor:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;#customer_message_processor.rb&lt;br /&gt;require 'processors/application'&lt;br /&gt;class CustomerMessageProcessor &lt; ApplicationProcessor&lt;br /&gt;&lt;br /&gt;  subscribes_to :customer_queue&lt;br /&gt;&lt;br /&gt;  def on_message(serialized_message)&lt;br /&gt;    customer_payload = YAML.load(serialized_message)&lt;br /&gt;    customer = Customer.new(:name =&gt; customer_payload.name)&lt;br /&gt;    customer.id = customer_payload.id&lt;br /&gt;    customer.save!&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This class will need access to the same CustomerPayload object as the Customers application when it deserializes it.&lt;br /&gt;&lt;br /&gt;In ActiveMessaging, a poller is used to poll the queue and trigger the on_message method shown above.  We'll need to start the poller in the Orders application using the following command:&lt;br /&gt;&lt;br /&gt;script/poller run&lt;br /&gt;&lt;br /&gt;And that's it.  If both the Orders and Customer applications are running, and have access to the ActiveMQ server, then when we create a Customer in the Customer application, it should show up in the Orders application a few moments later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-185582574694183476?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/185582574694183476/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=185582574694183476' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/185582574694183476'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/185582574694183476'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/06/asynchronous-messaging-with-rails_25.html' title='Asynchronous Messaging with Rails'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-2524345832328046984</id><published>2007-05-09T14:43:00.000-07:00</published><updated>2007-05-11T13:19:32.061-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='readability of tests'/><category scheme='http://www.blogger.com/atom/ns#' term='standard_params'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>New notation for standard_params</title><content type='html'>Andy Kotlinski came up with this new notation for &lt;a href="http://beechbonanza.blogspot.com/2007/02/standardparams-to-improve-readability.html"&gt;standard_params&lt;/a&gt;.  We found that over time, the use of standard_params became the default way to set up ActiveRecord objects in our test code, so Andy created a "new" and "create!" method, each of which take a class (or a symbol of the class name) as their first parameter.  Since our standard_params methods are named using the convention of [class_name]_standard_params, we simply create an object of the passed-in class, passing in the hash returned by the appropriate standard_params method.  We exclude any attributes that are passed in via the :without array, and merge any attributes that are passed in using the :merge hash.&lt;br /&gt;&lt;br /&gt;Using a class name:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;aeroplane = create! Aeroplane, &lt;br /&gt;                    :without =&gt; [:capacity, :safety_rating],&lt;br /&gt;                   :merge =&gt; {:model_number =&gt; "567890"}       &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using a symbol of the class name:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;aeroplane = create! :aeroplane, &lt;br /&gt;                    :without =&gt; [:capacity, :safety_rating],&lt;br /&gt;                   :merge =&gt; {:model_number =&gt; "567890"}&lt;br /&gt;&lt;/pre&gt;               &lt;br /&gt;Both of these versions will construct an Aeroplane object using the hash of parameters returned by the conventionally named method:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def aeroplane_standard_params  &lt;br /&gt;  { &lt;br /&gt;    :model_number =&gt; "123456", &lt;br /&gt;    :name =&gt; "A630", &lt;br /&gt;    :manufacturer =&gt; "Airbus", &lt;br /&gt;    :safety_rating =&gt; "5", &lt;br /&gt;    :capacity =&gt; "200"&lt;br /&gt;  }&lt;br /&gt;end &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;removing any attributes specified in the :without Array, and merging any attributes specified in the &lt;br /&gt;:merge Hash.&lt;br /&gt;&lt;br /&gt;We also added a :quantity attribute, which causes an Array of objects to be returned containing the number of objects specified.  For example, :quantity =&gt; 2 would cause two Aeroplane objects to be returned:&lt;br /&gt;&lt;pre class="code"&gt;                   &lt;br /&gt;plane1, plane2 = new :aeroplane, :quantity =&gt; 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The "new" and "create!" methods look like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def new(klass, options={})&lt;br /&gt;  new_or_create(:new, klass, options)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def create!(sym, options={})&lt;br /&gt;  new_or_create(:create!, sym, options)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def new_or_create(operation, sym, options={})&lt;br /&gt;  raise ApplicationError.new("Option keys must be one of: [:without, :merge, :quantity]") /&lt;br /&gt;    unless options.without(:without, :merge, :quantity).empty?&lt;br /&gt;  &lt;br /&gt;  klass, underscored = Class === sym ? [sym, sym.to_s.underscore] : [sym.to_s.camelize, sym]&lt;br /&gt;  src = &lt;&lt;-EOS&lt;br /&gt;    #{klass}.#{operation}(#{underscored}_standard_params.without(options[:without]).merge(options[:merge]||{}))      &lt;br /&gt;  EOS&lt;br /&gt;  &lt;br /&gt;  return eval(src) if options[:quantity].blank?&lt;br /&gt;  results = []&lt;br /&gt;  options[:quantity].times do&lt;br /&gt;    results &lt;&lt; (eval src)&lt;br /&gt;  end&lt;br /&gt;  results &lt;br /&gt;end    &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The notation also works well with nested objects.  A pilot object can be merged in, using the pilot_standard_params for construction:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt; plane_with_pilot = new :aeroplane, &lt;br /&gt;                        :merge =&gt; {&lt;br /&gt;                          :model_number =&gt; "12345",&lt;br /&gt;                          :pilot =&gt; new(:pilot)&lt;br /&gt;                        }&lt;br /&gt;&lt;/pre&gt;       &lt;br /&gt;&lt;br /&gt;One must be careful using Extract Method in tests.  If you hide away important setup information or assertion code, then failing tests can sometimes be more difficult to fix.  I make an exception in cases such as this, where unimportant information to the test is hidden away, thus making the important information more obvious to the reader.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-2524345832328046984?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/2524345832328046984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=2524345832328046984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2524345832328046984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2524345832328046984'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/05/new-notation-for-standardparams.html' title='New notation for standard_params'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-2664348798990069869</id><published>2007-03-29T09:33:00.000-07:00</published><updated>2007-03-29T09:42:30.301-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hiring'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby developer'/><category scheme='http://www.blogger.com/atom/ns#' term='thoughtworks'/><title type='text'>Thoughtworks is hiring ruby developers</title><content type='html'>Thoughtworks is looking for a &lt;a href="http://thoughtworks.com/jobs/Ruby-Specialist.html"&gt;Ruby Specialist&lt;/a&gt; and &lt;a href="http://thoughtworks.com/jobs/Ruby-Developers.html"&gt;Ruby Developers&lt;/a&gt;.  In fact, we're pretty much hiring for all positions at the moment (Java, .NET and and Buisness Analysts).  Send me an email at sharvie@thoughtworks.com if you have any questions, and mention my blog if you decide to apply.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-2664348798990069869?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/2664348798990069869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=2664348798990069869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2664348798990069869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2664348798990069869'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/03/thoughtworks-is-hiring-ruby-developers.html' title='Thoughtworks is hiring ruby developers'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-5974360226849892202</id><published>2007-03-24T14:56:00.000-07:00</published><updated>2007-03-30T12:09:47.834-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='with_constants'/><title type='text'>modifying constants in a class to aid testing</title><content type='html'>We needed a way to test pagination and sorting of lists across different pages in Rails controllers without having to create enough records to cross to the second page.  So we created a method to alter the value of a constant in a class that we are testing.  &lt;br /&gt;&lt;br /&gt;It enables us to test pagination and sorting (amongst other things) like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;class CarsController...&lt;br /&gt;&lt;br /&gt;  PER_PAGE = 20&lt;br /&gt;&lt;br /&gt;  def index&lt;br /&gt;    @pages, @cars = paginate :cars, :per_page =&gt; PER_PAGE, :order =&gt; "make, model" &lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;class CarsControllerTest &lt; Test::Unit::TestCase...&lt;br /&gt;&lt;br /&gt;  def test_index_sorts_by_make_and_then_model_for_page_1&lt;br /&gt;    car1 = Car.create!(standard_params.merge(:make =&gt; "Fiat", :model =&gt; "Uno"))  &lt;br /&gt;    car2 = Car.create!(standard_params.merge(:make =&gt; "Fiat", :model =&gt; "Bravo"))&lt;br /&gt;    car3 = Car.create!(standard_params.merge(:make =&gt; "Citroen", :model =&gt; "C2"))  &lt;br /&gt;&lt;br /&gt;    with_constants CarsController, :PER_PAGE =&gt; 2 do&lt;br /&gt;      get :index&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    assert_equal [car3, car2], assigns(:assets)&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def test_index_sorts_by_make_and_then_model_for_page_2&lt;br /&gt;    car1 = Car.create!(standard_params.merge(:make =&gt; "Fiat", :model =&gt; "Uno"))  &lt;br /&gt;    car2 = Car.create!(standard_params.merge(:make =&gt; "Fiat", :model =&gt; "Bravo"))&lt;br /&gt;    car3 = Car.create!(standard_params.merge(:make =&gt; "Citroen", :model =&gt; "C2"))  &lt;br /&gt;&lt;br /&gt;    with_constants CarsController, :PER_PAGE =&gt; 2 do&lt;br /&gt;      get :index, :page =&gt; 2&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    assert_equal_arrays [car1], assigns(:assets)&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;with_constants takes a class name and a hash of constant =&gt; value pairs, and yields to a block which is the code you want to call with the altered constants:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def with_constants(klass, constants_hash)&lt;br /&gt;  begin&lt;br /&gt;    old_constants = {}&lt;br /&gt;    constants_hash.each_pair do |name, value|&lt;br /&gt;      old_constants[name] = klass.send(:remove_const, name)&lt;br /&gt;      klass.const_set(name, value)&lt;br /&gt;    end&lt;br /&gt;    yield      &lt;br /&gt;  ensure&lt;br /&gt;    constants_hash.each_pair do |name, value|&lt;br /&gt;      klass.send(:remove_const, name)&lt;br /&gt;      klass.const_set(name, old_constants[name])&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-5974360226849892202?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/5974360226849892202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=5974360226849892202' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/5974360226849892202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/5974360226849892202'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/03/modifying-constants-in-class-to-aid.html' title='modifying constants in a class to aid testing'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-2876132644887598357</id><published>2007-03-22T15:42:00.000-07:00</published><updated>2007-03-25T09:09:50.222-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='readability'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='stubba'/><category scheme='http://www.blogger.com/atom/ns#' term='stub_object'/><title type='text'>wrapping stubba in stub_object method</title><content type='html'>The stub method in stubba gives me a warning when you stub :id ("Object#id will be deprecated; use Object#object_id"), which we seem to do fairly often with ActiveRecord objects. So we use a simple method called stub_object (which we wrote originally because we didn't know "stub" existed):&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def stub_object(params)&lt;br /&gt;  object = Object.new&lt;br /&gt;  params.each do |attr, value|&lt;br /&gt;    object.stubs(attr).returns(value)&lt;br /&gt;  end&lt;br /&gt;  object&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It gives us syntax like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;manufacturer = stub_object(:id =&gt; 5, :name =&gt; "Ford", :telephone_number =&gt; "555-888-999")&lt;br /&gt;Car.any_instance.expects(:manufacturer).returns(manufacturer)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And our manufacturer object will respond to any calls to :id, :name, or :telephone_number, without the warning for :id.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-2876132644887598357?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/2876132644887598357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=2876132644887598357' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2876132644887598357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2876132644887598357'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/03/wrapping-stubba-in-stubobject-method.html' title='wrapping stubba in stub_object method'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-2036381583213192126</id><published>2007-03-17T08:56:00.000-07:00</published><updated>2007-03-17T09:07:07.999-07:00</updated><title type='text'>cruisecontrol.rb released</title><content type='html'>&lt;a href="http://thoughtworks.com"&gt;Thoughtworks&lt;/a&gt; has released cruisecontrol.rb 1.0.  You can download it &lt;a href="http://cruisecontrolrb.thoughtworks.com/"&gt;here&lt;/a&gt;.  I know of a lot of rails projects that were rolling their own continuous integration, which lead Thoughtworks to put some resources into developing cruisecontrol.rb.  It takes about 10 minutes to set up, following rails' goal of convention over configuration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-2036381583213192126?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/2036381583213192126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=2036381583213192126' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2036381583213192126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/2036381583213192126'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/03/cruisecontrolrb-released.html' title='cruisecontrol.rb released'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-6740393208461382422</id><published>2007-03-05T20:38:00.000-08:00</published><updated>2007-03-25T09:32:51.638-07:00</updated><title type='text'>table_for erb template</title><content type='html'>Background:&lt;br /&gt;&lt;br /&gt;Rails provides a form_for method which can be used in a view to generate an html form.  When this is used, there is no need to specify the &amp;lt;form&amp;gt; tags and, when combined with the convenience methods such as text_field, select and radio_button, very little html needs to be handwritten.&lt;br /&gt;&lt;br /&gt;At my current project we create a lot of CRUD interfaces.  We found that we were often creating simple tables displaying attribute name-value pairs for a given object in the 'Read' view.  Inspired by the Rails Recipes 'tabular_form_for' template, my &lt;a href="http://kinderman.net"&gt;coding pair&lt;/a&gt; and I decided to create a table_for template.&lt;br /&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;br /&gt;Let's say an Aeroplane object has two attributes:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;   :name, :string&lt;br /&gt;   :model_number, :string&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;An rhtml view for displaying the Aeroplane object might look like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&amp;lt;table&amp;gt;&lt;br /&gt;   &amp;lt;tr&amp;gt;&lt;br /&gt;      &amp;lt;th&amp;gt;Aeroplane Name&amp;lt;/th&amp;gt; &amp;lt;td&amp;gt;&amp;lt;%=h @aeroplane.name %&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;   &amp;lt;/tr&amp;gt;&lt;br /&gt;   &amp;lt;tr&amp;gt;&lt;br /&gt;      &amp;lt;th&amp;gt;Model Number&amp;lt;/th&amp;gt; &amp;lt;td&amp;gt;&lt;%=h @aeroplane.model_number %&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;   &amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;%= link_to 'Edit', :action =&gt; 'edit', :id =&gt; @aeroplane %&gt; |&lt;br /&gt;&lt;%= link_to 'Back', :action =&gt; 'list' %&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We want to remove as much of that html code as possible.  A syntax like this would be nice:&lt;br /&gt;&lt;pre class="code"&gt;     &lt;br /&gt;&lt;% table_for @aeroplane do |t| %&gt;&lt;br /&gt;   &lt;%= t.print :name %&gt;&lt;br /&gt;   &lt;%= t.print :model_number %&gt;                &lt;br /&gt;   &lt;%  t.link_to 'Edit', url_for(:action =&gt; 'edit', :id =&gt; @aeroplane) %&gt;&lt;br /&gt;   &lt;%  t.link_to 'Back', url_for(:action =&gt; 'list') %&gt;        &lt;br /&gt;&lt;% end %&gt;&lt;br /&gt;&lt;/pre&gt;      &lt;br /&gt;We put the 'table_for' method in application_helper.rb, so that it is available to all templates.  It creates a TableForContext object to perform all the work for us, yields this object back to the caller, and wraps any output generated in 'table' tags.&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def table_for(object, &amp;block)&lt;br /&gt;   concat('&amp;lt;table&amp;gt;', block.binding)&lt;br /&gt;   context = TableForContext.new(object, block.binding)&lt;br /&gt;   yield context&lt;br /&gt;   concat(context.links + '&amp;lt;/table&amp;gt;', block.binding)&lt;br /&gt;   end&lt;br /&gt;&lt;/pre&gt;          &lt;br /&gt;The 'print' method in TableForContext is straight-forward:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def print(field)&lt;br /&gt;   "&amp;lt;tr&amp;gt;" + "&amp;lt;th&amp;gt;" + field.to_s.humanize + "&amp;lt;/th&amp;gt;" + "&amp;lt;td&amp;gt;#{@object.send(field)}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;"          &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;      &lt;br /&gt;We want the links to be side-by-side in a separate row at the bottom.  You'll notice in the view that the result of the 'print' method is output directly (it is wrapped in &lt;%= %&gt; tags), whereas the 'link_to' method is not (it is wrapped in &lt;% %&gt; tags).  The links are concatenated together to be output at the end:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def link_to(link_name, url)&lt;br /&gt;   @links &lt;&lt; ' ' unless @links.empty?&lt;br /&gt;   @links &lt;&lt; class =""&gt; 'button')", @view_binding)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All that is left is some customization options.  The ones we have required at so far are:&lt;br /&gt;&lt;br /&gt; - custom wrapping of the attribute value in other html (eg making the value link to another page).&lt;br /&gt; - custom headers (ie not just the attribute name humanized)&lt;br /&gt; - custom css classes on the table headers&lt;br /&gt;&lt;br /&gt;Let's say that an aeroplane has an associated Manufacturer object.  We add a manufacturer_id attribute to aeroplane (and associated belongs_to and has_many declarations).&lt;br /&gt;&lt;br /&gt;Let's say that the associated Manufacturer object has two attributes:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;   :name, :string&lt;br /&gt;   :address, :string&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that in the Aeroplane view we want to display the aeroplane's manufacturer name as a link to the manufacturer 'Show' view.  Let's also say that we want a custom label on our Aeroplane 'name' attribute, and a class called "header" on all of our table headers.&lt;br /&gt;&lt;br /&gt;With some changes to our 'table_for' and 'print' methods we should be able to accommodate the following syntax:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;% table_for @aeroplane, :header_class =&gt; "header" do |t| %&gt;&lt;br /&gt;   &lt;%= t.print :name, :label =&gt; 'Super Happy Aeroplane Name:' %&gt;&lt;br /&gt;   &lt;%= t.print :manufacturer do |manufacturer, aeroplane|&lt;br /&gt;      link_to manufacturer.name, manufacturer_url(:id =&gt; @aeroplane.manufacturer_id)&lt;br /&gt;   end %&gt;&lt;br /&gt;   &lt;%= t.print :model_number %&gt;                &lt;br /&gt;   &lt;%  t.link_to 'Edit', edit_aeroplane_url(:id =&gt; @aeroplane) %&gt;&lt;br /&gt;   &lt;%  t.link_to 'Back', aeroplanes_url %&gt;        &lt;br /&gt; &lt;% end %&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The 'table_for' method now looks like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def table_for(object, options={}, &amp;block)&lt;br /&gt;   concat('&amp;lt;table&amp;gt;', block.binding)&lt;br /&gt;&lt;br /&gt;   context = TableForContext.new(object, block.binding, options)&lt;br /&gt;   yield context&lt;br /&gt;&lt;br /&gt;   concat(context.links + '&amp;lt;/table&amp;gt;', block.binding)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The constructor for TableForContext becomes:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def initialize(object, view_binding, options={})&lt;br /&gt;   @object = object&lt;br /&gt;   @view_binding = view_binding&lt;br /&gt;   @links = ''&lt;br /&gt;   @options = options&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the 'print' method now looks like:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def print(field, options={})&lt;br /&gt;   label = options[:label].nil? ? field.to_s.humanize : options[:label]&lt;br /&gt;   if block_given?&lt;br /&gt;      content = yield @object.send(field), @object&lt;br /&gt;   else&lt;br /&gt;      content = @object.send(field)&lt;br /&gt;   end&lt;br /&gt;   "&amp;lt;tr&amp;gt;" + th(label) + "&amp;lt;td&amp;gt;#{content}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;"&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We have added a private method 'th' which adds the header class, if specified:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def th(content)&lt;br /&gt;   html = '&amp;lt;th'&lt;br /&gt;   if @options[:header_class]&lt;br /&gt;      html &lt;&lt; " class=\"#{@options[:header_class]}\""&lt;br /&gt;   end&lt;br /&gt;   html &lt;&lt; "&gt;#{content}&amp;lt;/th&amp;gt;"&lt;br /&gt;end  &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-6740393208461382422?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/6740393208461382422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=6740393208461382422' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6740393208461382422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/6740393208461382422'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/03/tablefor-erb-template.html' title='table_for erb template'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2813261110497121234.post-7870556691731764905</id><published>2007-02-08T13:00:00.000-08:00</published><updated>2007-07-05T17:10:40.352-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='readability of tests'/><category scheme='http://www.blogger.com/atom/ns#' term='standard_params'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>standard_params to improve readability of tests</title><content type='html'>I beleive this came from the &lt;a href="http://www.pragmaticprogrammer.com/"&gt;Pragmatic Programmers&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For each of our ActiveRecord model objects, we have a private test method within the test class called "standard_params".  It returns a Hash containing parameters which, if passed to the constructor of the ActiveRecord object, will result in the construction of a valid object.&lt;br /&gt;&lt;br /&gt;The benefits of this method are two-fold:&lt;br /&gt;&lt;br /&gt;1.  It improves the readability of tests.  If the ActiveRecord object has a long list of attributes, and you are testing behaviour which is dependent only on one or two of these attributes, you do not have to explicitly specify all of the parameters - you simply merge in the ones that you really care about.&lt;br /&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;br /&gt;Let's say an Aeroplane object is invalid unless it has a 6 digit :model_number.  It also has a list of required attributes, such as :name, :manufacturer, :safety_rating and :capacity. It would be invalid without any of these attributes. &lt;br /&gt;&lt;br /&gt;Without the standard_params method, our test for invalidity without a six-digit model number would look like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def test_invalid_without_six_digit_model_number&lt;br /&gt;   aeroplane = Aeroplane.new(:model_number =&gt; "12345", :name =&gt; "A630", :manufacturer =&gt; "Airbus", :safety_rating =&gt; "5", :capacity =&gt; "200")&lt;br /&gt;   assert_equal false, aeroplane.valid?&lt;br /&gt;   assert_equal false, aeroplane.errors.invalid?(:model_number)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;With standard_params it would change to:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def test_invalid_without_six_digit_model_number&lt;br /&gt;   aeroplane = Aeroplane.new(standard_params.merge(:model_number =&gt; "12345"))&lt;br /&gt;   assert_equal false, aeroplane.valid?&lt;br /&gt;   assert_equal false, aeroplane.errors.invalid?(:model_number)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;private&lt;br /&gt;&lt;br /&gt;def standard_params&lt;br /&gt;   {&lt;br /&gt;       :model_number =&gt; "123456",&lt;br /&gt;       :name =&gt; "A630",&lt;br /&gt;       :manufacturer =&gt; "Airbus",&lt;br /&gt;       :safety_rating =&gt; "5",&lt;br /&gt;       :capacity =&gt; "200")&lt;br /&gt;   }&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;:model_number is the only attribute explicitly specified in the test, thus removing a lot of "noise" in the test setup data - something which aids readability.&lt;br /&gt;&lt;br /&gt;The second benefit is:&lt;br /&gt;&lt;br /&gt;2. Tests are &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;-er (in a good way).  I'm a firm believer that tests do not necessarily need to be DRY - readability is far more important.  "Extract method" in tests can often be your worst enemy if done in a naive way.  I think it is acceptable in this case because the part of the setup data that is extracted is not important to the test - that which *is* important is merged explicitly into the Hash in the test method itself. And so the usual benefits of DRY-ness result - when another attribute is added to the model object which itself has associated validations, the test methods that require construction of valid objects do not have to change - only the standard_params method needs to change.&lt;br /&gt;&lt;br /&gt;We also added a "without" method to Hash, which removes elements specified in the parameters:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;module HashExtension&lt;br /&gt; def without(*keys)&lt;br /&gt;   clone = self.dup&lt;br /&gt;   keys.each { |key| clone.delete(key) }&lt;br /&gt;   clone&lt;br /&gt; end &lt;br /&gt;end&lt;br /&gt;Hash.send :include, HashExtension&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We use this in combination with standard_params to test for validates_presence_of clauses.  Eg&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;def test_invalid_without_name&lt;br /&gt;   aeroplane = Aeroplane.new(standard_params.without(:name))&lt;br /&gt;   assert_equal false, aeroplane.valid?&lt;br /&gt;   assert_equal false, aeroplane.errors.invalid?(:name)   &lt;br /&gt;end &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2813261110497121234-7870556691731764905?l=www.shaneharvie.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.shaneharvie.com/feeds/7870556691731764905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2813261110497121234&amp;postID=7870556691731764905' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7870556691731764905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2813261110497121234/posts/default/7870556691731764905'/><link rel='alternate' type='text/html' href='http://www.shaneharvie.com/2007/02/standardparams-to-improve-readability.html' title='standard_params to improve readability of tests'/><author><name>Shane Harvie</name><uri>http://www.blogger.com/profile/01016971334424471677</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://farm1.static.flickr.com/182/394043885_0f2634cf9b.jpg?v=0'/></author><thr:total>4</thr:total></entry></feed>
