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.