patternrubyMinor
Writing successive function calls
Viewed 0 times
callswritingfunctionsuccessive
Problem
I have a Ruby function which generates and saves a report. I'm looking for a Clean Code way of writing this:
But I hate single-use low-semantic-value variables like these. So here's my first re-write:
And even though the flow is now in the reverse order, I find this quicker to grasp. There's less junk getting in the way.
But maybe a map/reduce or block style of programming would provide the same clarity but show data flow in the direction written, top to bottom.
EDIT: I re-wrote it so that the code can be read mostly top-to-bottom, not bottom-to-top:
corpus.rake — the reporting script
violation.rb — the Violation class
...enabled by object-oriented refactoring; moving the functions into the Violations object enabled method chaining. One part I don't like is that
def generate
violations = relevant_violations
comments = convert_to_cleaned_up_comments(violations)
text = convert_to_text(comments)
save_to_file(text)
endBut I hate single-use low-semantic-value variables like these. So here's my first re-write:
def generate
save_to_file(
convert_to_text(
convert_to_cleaned_up_comments(
relevant_violations)))
endAnd even though the flow is now in the reverse order, I find this quicker to grasp. There's less junk getting in the way.
But maybe a map/reduce or block style of programming would provide the same clarity but show data flow in the direction written, top to bottom.
EDIT: I re-wrote it so that the code can be read mostly top-to-bottom, not bottom-to-top:
corpus.rake — the reporting script
def generate_and_save
save_to_file Violation
.select(&:within_past_30_days?)
.map(&:to_corpus_friendly_comment)
.join "\n"
end
def save_to_file(text)
File.open(LOCAL_PATH, 'w') { |f| f.write text }
endviolation.rb — the Violation class
def to_corpus_friendly_comment
facts
.downcase
.gsub(/\b(\d+f?|\w|pic|inspection)\b/, ' ')
.gsub(/\W/, ' ')
.gsub(/(foods?|please|days|use)/, '')
.gsub(/ +/, ' ')
.strip
end
def within_past_30_days?
date > 30.days.ago
end...enabled by object-oriented refactoring; moving the functions into the Violations object enabled method chaining. One part I don't like is that
.join "\n" is a lower level of abstraction than the other lines in generate_and_save.Solution
I honestly think your first version is the best one. It's readable, and makes more sense at a glance than the refactorings. That said, I would consider a couple of things.
The
I would consider if generating the report is the
The idea is to isolate presentation logic that is only needed for the report, in the report class itself. You then have cleanly defined responsibilities for each class.
def generate
violations = relevant_violations
comments = convert_to_cleaned_up_comments(violations)
text = convert_to_text(comments)
save_to_file(text)
endThe
comments and text temp variables represent a transitional state. I would ask myself if I will use them in any other form. If not, and if their entire reason for existence is to transition to text, I would do away with them and have one entry point for clean, or savable text.I would consider if generating the report is the
Violation class responsibility. The report is a presentational concern, and I feel it should have its own class.class ViolationsReport
attr_reader :violations, :file
def initialize(violations, file)
@violations = violations
@file = file
end
def generate
save_to_file formatted_violations
end
private
def formatted_violations
convert_to_comments violations
end
def save_to_file(text)
file.open(LOCAL_PATH, 'w') { |f| f.write text }
end
# more methods here to do the job
endThe idea is to isolate presentation logic that is only needed for the report, in the report class itself. You then have cleanly defined responsibilities for each class.
Code Snippets
def generate
violations = relevant_violations
comments = convert_to_cleaned_up_comments(violations)
text = convert_to_text(comments)
save_to_file(text)
endclass ViolationsReport
attr_reader :violations, :file
def initialize(violations, file)
@violations = violations
@file = file
end
def generate
save_to_file formatted_violations
end
private
def formatted_violations
convert_to_comments violations
end
def save_to_file(text)
file.open(LOCAL_PATH, 'w') { |f| f.write text }
end
# more methods here to do the job
endContext
StackExchange Code Review Q#74438, answer score: 7
Revisions (0)
No revisions yet.