Skip to content

Instantly share code, notes, and snippets.

@CoryFoy
Last active August 29, 2015 13:57
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save CoryFoy/9441665 to your computer and use it in GitHub Desktop.
require 'set'
GEM_FILE_TO_PROCESS = "Gemfile.lock"
def gem_list_finished?(line)
line.include?("PLATFORMS")
end
def non_gem_line?(line)
line.include?("GEM") ||
line.include?("remote:") ||
line.include?("specs:") ||
line.empty?
end
def gem_name_without_version(line)
line.split(' ').first
end
def get_files_in_gem(gem)
#gem contents returns the files
#as a line break delimited string
`gem contents #{gem}`.split
end
def line_count_for_file(file)
output = `wc -l #{file}`
#line count is the first column from
#the returned value
output.strip.split(' ').first.to_i
end
def log(message)
puts message
end
gemfile_list = File.read(GEM_FILE_TO_PROCESS)
gems_to_process = Set.new
total_line_count = 0
gemfile_list.each_line do |gem_line|
gem_line = gem_line.strip
break if gem_list_finished?(gem_line)
next if non_gem_line?(gem_line)
gems_to_process.add(gem_name_without_version(gem_line))
end
log "TOTAL GTP: #{gems_to_process.count}"
gems_to_process.each do |gem|
log "Processing #{gem}"
contents = get_files_in_gem(gem)
gem_line_count = 0
contents.each do |file|
gem_line_count += line_count_for_file(file)
end
log " LOC: #{gem_line_count}"
total_line_count += gem_line_count
end
log "Total Lines: #{total_line_count}"
@jbrains
Copy link

jbrains commented Mar 9, 2014

I teach design through the metaphor of listening to the code. Signals from names are usually harder to understand than signals from duplication, and so when I find an example of the former, I like to highlight it. I find one here.

I see this pattern frequently: a purely structurally-named thing, often so named to avoid a naming collision, indicates some code worth extracting.

local... but local what? Local total (of lines), I guess. There doesn't seem to be a pithy, precise name for this, which hints that we've put it in a larger scope than appropriate. Here, it has a pithy, imprecise name. If I wanted to be cruel, I'd call it lazy. I've been that lazy. You've been that lazy. "It'll be fine."

So I rename local to something like line_count_for_this_gem, and then I notice total matches the same pattern and rename total to total_line_count. When I say "make names more precise", I mean something like this. Now the latent structure slaps me in the face: to avoid the verbose names and duplication, I extract a function for lines 20-28 computing line_count_for_this_gem. I call the function countLinesForGem. Now, within this scope, we can name things more pithily without losing precision. Alternatively, we can remove the newly-introduced duplication between the name of the new function and words in the more-precise names of these variables.

Inside the new function, I notice amount, which exhibits the same pattern. I extract a function for lines 23-27. Even better than renaming the resulting temporary variables, they can probably disappear. (That comes down to personal preference for me.)

Of course, none of this accounts for the choice of the name GFILE. That's just wrong.[1]


[1] I can't be entirely humorless; you shouldn't, either. That includes your reaction to this footnote.

@CoryFoy
Copy link
Author

CoryFoy commented Mar 9, 2014

It should be noted that JB's post above was in response to version 2 of this Gist. The above version attempts to take into account better code principles, and I blogged about it at: Put Your Best Code Forward http://blog.coryfoy.com/2014/03/putting-your-best-code-forward/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment