There are many business requirements that we need to remember or ensure that gets done as developers which are not necessarily related to code or the behavior of the software. This could be code comments, copyright notices, or other information that are not necessarily executable, but part of the source code. For these types of requirements, we used rSpec to explicitly check whether the behavior-less requirement was met throughout our code base.

On one of the projects we have been working, our client has requested that we add a header with a copyright notice to the top of each file. The header was a few lines long, so we created a TextMate snippet to make it easy to generate when creating a new file. Unfortunately, this did not help us remember to actually add the header to each new file. After continually forgetting to add the header, we decided to create a spec which checks that all files have the appropriate header to help us remember.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
describe "copyright header" do
  it "should be required on all ruby files" do
    vendor = %w[
      db/schema.rb lib/tasks/rspec.rake ...
    ]
    
    (Dir["{app,lib,db,spec,features}/**/*.{rb,rake}"] - vendor).each do |filename|
      filename.should have_ruby_copyright_header
    end
  end
  
  it "should be required on all javascript files" do
    vendor = %w[
      public/javascripts/all.js public/javascripts/prototype.js ...
    ]
    
    (Dir["public/javascripts/**/*.js"] - vendor).each do |filename|
      filename.should have_javascript_copyright_header
    end
  end
  
  it "should be required on all stylesheet files" do
    vendor = %w[
      public/stylesheets/all.css public/stylesheets/blueprint/screen.css ...
    ]
    
    (Dir["public/stylesheets/**/*.css"] - vendor).each do |filename|
      filename.should have_stylesheet_copyright_header
    end
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class CopyrightHeaderMatcher
  def initialize(comment_line, comment_start = nil, comment_end = nil)
    @header = <<-EOHEADER
#{comment_start || comment_line} Copyright (c) 2008 Your Organization
#{comment_line}
#{comment_line} Possession of a copy of this file grants no permission or license
#{comment_line} to use, modify, or create derivate works.
#{comment_line} Please visit http://www.example.com/contact for further information.
EOHEADER
    @header << "#{comment_end}n" if comment_end
  end
  
  def matches?(filename)
    @filename = filename
    File.read(filename).match(Regexp.new(Regexp.escape(@header))) ? true : false
  end
  
  def failure_message
    "expected #{@filename} to have the header:n#{@header}"
  end
end
 
def have_ruby_copyright_header
  CopyrightHeaderMatcher.new("#")
end
 
def have_javascript_copyright_header
  CopyrightHeaderMatcher.new("//")
end
 
def have_stylesheet_copyright_header
  CopyrightHeaderMatcher.new(" *", "/*", " */")
end

Now when we forget to add the header, we get a nice reminder when running the specs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ script/spec spec/code/header_spec.rb
F..
1)
'copyright header should be required on all ruby files' FAILED
expected app/models/user.rb to have the header:
# Copyright (c) 2008 Your Organization
#
# Possession of a copy of this file grants no permission or license
# to use, modify, or create derivate works.
# Please visit http://www.example.com/contact for further information.
 
Finished in 0.314035 seconds
 
3 examples, 1 failure

Problem solved.

technology logo

Get a Free Consultation

Your Free Consultation will be packed full of discussions, brainstorming, and hopefully, excitement. The meeting is designed to help uncover your challenges, define your needs, and outline possible solutions so you can make decisions that will lead to the business outcomes you desire.