Hash.class_eval do
# TODO: unit test this
def find_and_replace( x, y )
self.dup.find_and_replace! x, y
end
def find_and_replace!( x, y )
self.each_pair do |k,v|
if v.is_a?(Hash)
v.find_and_replace! x, y
else
if x.is_a?(Regexp) && v.is_a?(String)
v.gsub!( x, y )
else
v = y if v == x
end
self[k] = v
end
end
end
end
I recently had a problem with rake gems:install with a fresh rails app. Rakefile bootstraps the full rails environment, including plugins, controllers, etc. Some of the code getting executed depended on a gem, but I was running “rake gem:install” to install those gems. A circular “chicken or the egg” problem!
Here was my solution, which I put in environment.rb
unless $0 =~ /rake/
# ... require code that relies on certain gems
end
Just because you’re using Ruby on Rails doesn’t make you agile. That’s why we created ScrumNinja, a lightweight project management tool that helps you manage your project like a ninja. ScrumNinja is built on Ruby on Rails by a team of Scrum practitioners. Its simple yet powerful features makes managing your project easier. Also, check out the resources like our Scrum tutorial to get you started.
Scrum has some great things to teach us about how to work with each other and how to be more effective. Here are six of them:
1. Always work from a prioritized list
Have you ever thought about how much of your day is wasted doing things that don’t mean very much? Or, do you engage too much in nerd pleasure i.e. working on things that are technically satisfying, but not necessarily working on what is important to your customer or client? This one simple act of working from a prioritized list will change the way you function and make you more effective. Keep the list up-to-date daily with the most important things on the top. Then start knocking them off.

2. Make a commitment publicly
If you have a task that you want to get done, commit to it and tell someone. In Scrum you would make the commitment to your team members, but your dog might work just as well (just kidding about that, you should probably just stick to humans). Something magic happens when that commitment is made and it is more likely to get done than if you didn’t make it.
3. Get feedback regularly
In Scrum this is called a ‘Retrospective’ and it should be done on a regular basis (regular being key). Ask someone you are working with or your boss at regular intervals (e.g. every 2 weeks, every month) to have lunch with you and give you some feedback. What’s it like working with me? What do you like about working with me? Am I doing anything that’s not working for you? What can I do to improve myself? You can also do it in a more ad-hoc way as long as it works out being regular. For example, if you just finished pair programming or just had a meeting, ask the other person ‘what went well?’ and ‘what could be improved?’
4. Time-box your activities
Setting time limits on meetings and tasks will help you keep focused. With Scrum, the concept of a ‘sprint’ assures that you commit to a set of functionality that is time-boxed. Here’s a golden nugget for you: tasks always tend to shrink or expand to fit the amount of time allotted (cf. Parkinson’s Law). So give yourself a shorter, but realistic allotment of time, then reassess when you hit the limit. If working with a client/product owner, this is a good time to communicate the status of the project and make those trade-offs – should you simplify the feature, remove it, or keep going on the current path. Of course, if you keep working, other features will be pushed out because something’s gotta give.
5. Work sustainably
Startup companies never seem to remember this one. Agile has zero to do with working yourself until you’re burned out and has everything to do with working sustainably. The number of hours we have in a week is fixed – if something is added to the current iteration, something else has to be pushed out. If this is the week of a critical release then there may be an exception, but having critical releases on a weekly basis no longer qualifies as critical.

6. Make your work visible
Use a push, not a pull system of project management. Managers who constantly poll their developers for their project status only interrupt developer work flow. The reason that they do this is because they can’t keep their minds wrapped around all the moving parts of the project. If developers take it upon themselves to push their current status to a place that everyone has visibility into, then the need for micro-management is gone. So, whether you use a product like ScrumNinja or index cards and a cork board, your manager and other stakeholders will be up-to-date on progress.
So in a flash of inspiration on the airplane last week, I jotted down a new version of my shoulda params pattern. Put this pastie in test/shoulda_macros/request_params.rb
The general idea is this:
a_get_to(:index) do
with_params( {:foo => 'bar'}, "valid params" ) do
should_respond_with :success
with_params( :more => 'nested stuff' ) do
should "do something extra special" do
assert true
end
end
end
end
Here’s what it looks like in action, taken from the ScrumNinja.com code:
require File.dirname(__FILE__) + '/../test_helper'
class MainControllerTest < ActionController::TestCase
@@message = "you guys rock!"
context "When not logged in" do
setup do
login_as( nil )
end
%w[ index tour contact api ].each do |action|
a_get_to( action, params=false ) do
should_respond_with :success
end
end
a_get_to( :feedback, params=false ) do
should_redirect_to "'/login'"
end
a_post_to( :contact, :message => @@message ) do
should_send_email :to => "Mailer::GO_EMAIL", :body => %r{#@@message}
should_redirect_to "'/'"
end
end
context "When logged in" do
setup do
login_as( @user = create_user )
end
a_post_to( :feedback, :message => @@message ) do
should_send_email :to => "Mailer::GO_EMAIL", :body => %r{#@@message}
should_respond_with :redirect
end
end
end
We are officially in public beta! Here’s the message that went out to all our users. We’ve made lots of improvements to the system. Please check it out!
Happy New Year! We’re officially in public beta. Check out the new improvements we’ve made to make ScrumNinja a more enjoyable experience:
- UI look and feel improvements – We know it’s gotta look and feel good to you.
- More responsive prioritization
- Ability to drag stories in sprint history
- Story and Task IDs more prominent and visible
- Edit permissions based on account type
- User Directory
- Visitor read-only account type
- Chores need not be accepted after delivery
- Ability to add a stories from the card wall
- Installed UserVoice feedback widget so you can add and vote on features and bugs
We’ve also added application monitoring services from Scout (scoutapp.com) so that we can keep an eye on our slice and make sure that it is up as much as possible for those of you who depend on us.
We’re excited about 2009 and hope to make ScrumNinja even better. Thank you for all of your feedback. Keep it coming and keep on Scruming!
Best Regards, Rodney & David The ScrumNinja team!I see so many philosophical discussions about Scrum. Is it a Panacea? Does it actually work? Is it better than XP? In truth, I believe that many of these discussions are a waste of time. The reason being is that Scrum is useless unless you put it in action. Scrum is just a framework or a lightweight process by which one develops software. The essence of Scrum is not native to software development. In fact, the more exposure I have to other areas the more I see Scrum as a successful pattern that can be applied to many areas of life and work.
I have two examples that I have seen in the last year that are processes similar to Scrum. One example is C.J. Hayden’s book called Get Clients Now. It uses a process that is similar to Scrum only it is applied to sales and getting more clients. Another example I have seen recently is Dan Olsen’s process of designing a killer web app. This method uses a continuous integration process that looks very much like Scrum to figure out how to drive users to certain goals in your app.
I’m sure you can find many more examples of Scrum being used in different areas. Please post comments of others that you know of.
P.S. We are looking for Alpha testers for ScrumNinja. If you are interested, please sign up.
Just another little thing I love about ruby and the way it follows the Principle of Least Surprise: command history searching in irb with ctrl-R.
[aurora:~/work/scrumninja/trunk] dfl% irb
(reverse-i-search)`find': Project.find(1).stories.each(&:destroy)
I <3 Ruby! :)
We are proud to announce the alpha of our very first product!
ScrumNinja: a hosted project management tool built from scratch with Scrum in mind.
Here is the message we sent to our announcement list:
We are excited to announce the Alpha version of ScrumNinja. The best way to see the value of this tool is to commit to using it for a real project, even just a small one. ScrumNinja will always be free for one project and up to three users. For now, there are no limits to the number of users or projects. Please check out the quick start guide to get started:
If you’d like to become an official tester and be considered for a $50 credit, please let us know. Official testers actively use the product to manage a real project, give us feedback and occasionally answer simple questionnaires.
Regards, Rodney and DavidFactories-and-workers is now updated to not only be a plugin, but also a gem. Thanks to Jonathan Barket and Nick Hoffman for their help!
It is also now automatically loaded for test and development environments (as a plugin, or do it manually as below with the unless statement). I find this really comes in handy for using it in the rails console to populate your database.
Get It
Add it as a gem dependency to your rails environment.rb:
config.gem 'dfl-factories-and-workers', :lib => 'factories-and-workers', :source => 'http://gems.github.com' unless RAILS_ENV=='production'
Or install it as a gem manually:
gem sources -a http://gems.github.com sudo gem install dfl-factories-and-workers
Or grab the source:
git clone git://github.com/dfl/factories-and-workers.git
so I’m surprised I didn’t figure this out earlier… it was this message that finally drove the point home.
When using haml in your rails view templates, you do not need to close ruby blocks and conditional statements.
for example:
Here's how I was doing it before:
- content_for :head do
= stylesheet_link_tag 'jquery.autocomplete'
= javascript_include_tag 'jquery.autocomplete'
- end
- javascript_behaviour '$("input#user_full_name").autocomplete("project_roles/auto_complete_for_user_full_name")'
And here's how it really should be:
- content_for :head do
= stylesheet_link_tag 'jquery.autocomplete'
= javascript_include_tag 'jquery.autocomplete'
- javascript_behaviour '$("input#user_full_name").autocomplete("project_roles/auto_complete_for_user_full_name")'
So much nicer and readable, just as the haml gods intended :)
+1 for making ”- end” an error.
UPDATE:
On Oct 10, 2008, at 5:18 PM, Nathan Weizenbaum wrote: ”- end” actually is an error in the most recent Haml release, 2.0.3.
I shared some tips with a colleague who is relatively new to rails, and thought I’d post them here too.
##before:
content_tag :div, @event.description_html unless @event.description_html.to_s.blank?
## better:
# blank works on nil, string, array, and hash. It's a rails thing from ActiveSupport.
content_tag :div, @event.description_html unless @event.description_html.blank?
## best:
#But even better is to use the special #{attribute}? methods which we get with Rails 2. These basically just call !#{attribute}.blank?
content_tag :div, @event.description_html if @event.description?
the returning method is also handy at times… also from ActiveSupport. Here is an abstracted example from a rails helper: I’m not sure why yet, but returning doesn’t seem to work with strings
## before:
html = ""
html += "some stuff" if some_condition
html += "123"
html
## after:
returning [] do |html|
html << "some stuff" if some_condition
html << "123"
end.join("\n")
and also the pluralize method is part of ActionView::Helpers
## before:
def people_or_person(count)
(count == 1) ? 'person' : 'people'
end
## after:
pluralize count, "person"
using case…
## before:
word.each_char do |x|
if ((x =~ /[a-zA-Z]/) == 0)
str << "[#{x.to_s.downcase}#{x.to_s.upcase}]"
else
str << "[#{x}]"
end
end
## after:
word.each_char do |x|
case x
when /[a-zA-Z]/
str << "[#{x.to_s.downcase}#{x.to_s.upcase}]"
else
str << "[#{x}]"
end
end
using map_with_index (or the closest thing we have in Ruby 1.8) aka collect_with_index
## before:
roles = []
@events_summary[:role_groups].each_with_index do |role, idx|
roles << [role[:for], idx]
end
roles
## after:
require 'enumerator'
@events_summary[:role_groups].enum_with_index.map{ |role, idx| [ role[:for], idx ] }
## Ruby 1.9:
@events_summary[:role_groups].map.with_index{ |role, idx| [ role[:for], idx ] }
require 'rubygems'
require 'activesupport'
# this doesn't work as expected. does anyone know why?
z = returning html="" do
html += "stuff"
html += "more stuff" if true
end
p z
#>> ""
# this works though
z = returning html=[] do
html << "stuff"
html << "more_stuff" if true
end.join("\n")
p z
#>> "stuff\nmore_stuff"
Update: Thanks DrMark, I should have done it like this:
(I didn’t realize that one could use << on strings)
z = returning html="" do
html << "stuff"
html << " more stuff" if true
end
So I just switched over one of our projects to jQuery this weekend, to see what all the hype is about. It’s pretty good. Most notable is that jQuery $() is the same as Prototype $$() [find by CSS selector]. There seems to be no equivalent to Prototype’s $() [find by dom id]. The switch is as simple as adding # [dom id selector] in front of your old prototype $() methods.
jRails is pretty good, I’d say it was 95% plug and play. Just a few quirks where I had to modify some parameters to my helper method calls.
I previously posted about global params hashes for nested shoulda contexts in functional tests. This post is a followup that shows how my solution has since crystallized.
Here’s the special sauce to add to your test_helper.rb:
ActionController::TestCase.class_eval do
# special overload methods for "global"/nested params
[ :get, :post ].each do |overloaded_method|
define_method overloaded_method do |*args|
action,params,extras = *args
super action, @params.merge( params || {} ), *extras
end
end
def setup
super
@params = {}
end
end
This creates a @params variable that gets merged with all HTTP requests.
Now you can use global params in your nested contexts as such:
class FooControllerTest < ActionController::TestCase
def setup
super
@params[:security_token] = 'abc123' # add any global params you need here
@event = create_event
end
context "A POST to :action" do
setup do
@action = lambda{ post :action, :id => @event.id }
end
%w[ attending not_attending maybe_attending ].each do |status|
context "with :status = '#{status}'" do
setup do
@params[:status] = status
@action.call
end
should_respond_with :success
should_change "Rsvp.count", :by => 1
should "create the proper Rsvp object" do
assert Rsvp.find_by_user_id_and_event_id_and_status( @user.id, @event.id, status )
end
end
end
end
end

