Automatically Printing Rake (or other Ruby) Variables
The FakeItEasy rakefile contains a vars
target
(brainchild of Adam Ralph) that can be used to print out
the local variables defined in the script. Mostly these are static
variables, such as the path to the NUnit command, but some,
such as the upcoming FakeItEasy version, are computed. Logging these
computed variables can help debug misbehaving builds.
If ever something goes wrong, we can check the TeamCity build log and see something like this:
assembly_info: Source/CommonAssemblyInfo.cs mspec_command: Source/packages/Machine.Specifications.0.8.0/tools/mspec-clr4.exe nuget_command: Source/packages/NuGet.CommandLine.2.8.0/tools/NuGet.exe nunit_command: Source/packages/NUnit.Runners.2.6.3/tools/nunit-console.exe nuspec: Source/FakeItEasy.nuspec output_folder: Build repo: FakeItEasy/FakeItEasy solution: Source/FakeItEasy.sln ssl_cert_file_url: http://curl.haxx.se/ca/cacert.pem version: 1.21.0 integration_tests: Source/FakeItEasy.IntegrationTests/bin/Release/FakeItEasy.IntegrationTests.dll Source/FakeItEasy.IntegrationTests.VB/bin/Release/FakeItEasy.IntegrationTests.VB.dll release_body: * **Changed**: _<description>_ - _#<issue number>_ * **New**: _<description>_ - _#<issue number>_ * **Fixed**: _<description>_ - _#<issue number>_ With special thanks for contributions to this release from: * _<user's actual name>_ - _@<github_userid>_ release_issue_body: **Ready** when all other issues forming part of the release are **Done**. - [ ] run code analysis in VS in *Release* mode and address violations (send a regular PR which must be merged before continuing) - [ ] check build, update draft release in [GitHub UI](https://github.com/FakeItEasy/FakeItEasy/releases) including release notes, mentioning non-owner contributors, if any …
Originally, the vars
task was hand-written, so
whenever we added a new variable we had to update the task. Not too
long ago, I added a new variable, and (surprisingly) remembered to update
vars
. However, Adam noticed that I had put the puts
statement in
the task in the wrong place, so the declaration order didn't match the
printed order. A small thing, but the small things matter.
So, we had a chat about the best way to present the
variables. Declaration order is attractive, but I pushed a different
approach: first, separating the variables with short values, such as
assembly_info
, from variables with long values, such as
release_body
. This keeps the short values from becoming lost in the
noise of the longer ones. Second: sort lexicographically within the
groups, to aid scanning.
We came to an agreement, but as I started to make the change, I
thought, "Why make humans worry about this? Computers are good at
partitioning and sorting." So, after a quick search for something that
would allow printing of local Ruby variables, I found
local_variables
, and rewrote the task:
desc "Print all variables"
task :vars do
print_vars(local_variables.sort.map { |name| [name.to_s, (eval name.to_s)] })
end
def print_vars(variables)
scalars = []
vectors = []
variables.each { |name, value|
if value.respond_to?('each')
vectors << [name, value.map { |v| v.to_s }]
else
string_value = value.to_s
lines = string_value.lines
if lines.length > 1
vectors << [name, lines]
else
scalars << [name, string_value]
end
end
}
scalar_name_column_width = scalars.map { |s| s[0].length }.max
scalars.each { |name, value|
puts "#{name}:#{' ' * (scalar_name_column_width - name.length)} #{value}"
}
puts
vectors.each { |name, value|
puts "#{name}:"
puts value.map {|v| " " + v }
puts ""
}
end
Points of interest:
- The task delegates to a function right away, to avoid creating new
variables that would be found by
local_variables
. - The first thing the method does is partition variables into "scalars", to be rendered on the same line as the variable name, and "vectors", which have multiple elements or lines, and are rendered below the variable name.
- As a bonus, the scalar variable names padded so the values can all land on a "tab stop"
Best of all, now we can add rake variables willy-nilly, with nary a thought about printing them out. It just happens.