Reputation: 38
I'm wondering if there's a way to refactor this method to minimize the levels of indentation. After reading clean code Uncle Bob says once a method gets past two levels it becomes harder to understand what's going on.. I don't think this method is overly complex, but I think it could be cleaned up and look more like a story.
here is my method
def sync!(xml_document, language_id, status_id, file)
survey = sync_survey.(xml_document, status_id)
sync_survey_label.(xml_document, survey.id, language_id, file)
xml_document.xpath('//page').each do |xml_section|
section = sync_section.(xml_section, survey.id)
sync_section_label.(xml_section, section.id, language_id)
xml_section.xpath('.//question_group').each do |xml_question_group|
question_group = sync_question_group.(xml_question_group, section.id)
sync_question_group_label.(xml_question_group, question_group.id, language_id)
xml_question_group.xpath('.//question').each do |xml_question|
question = sync_question.(xml_question, question_group.id)
sync_question_label.(xml_question, question.id, language_id)
xml_question.xpath('.//answerchoice').each do |xml_choice|
choice = sync_choice.(xml_choice, question.id)
sync_choice_label.(xml_choice, choice.id, language_id)
end
end
end
end
survey
end
So, Im traversing an xml and saving to the corresponding methods. each survey has many sections which has many question_groups which has many questions which has many choices.
I know I could extract each loop into a method but then I would still have 4 levels of indentation.
I want to do something like...
def sync!(xml_document, language_id, status_id, file)
survey = sync_survey.(xml_document, status_id)
sync_survey_label.(xml_document, survey.id, language_id, file)
sync_sections.(xml_document)
sync_section_labels.(xml_document, language_id)
sync_question_groups.(xml_document)
sync_question_group_labels.(xml_document, language_id)
sync_questions.(xml_document)
sync_question_labels.(xml_document, language_id)
sync_choices.(xml_document)
sync_choice_labels.(xml_document, language_id)
survey
end
Are there any ideas to do something like this or make this code more readable? Is it even necessary to refactor it? Any ideas are welcome.
Here is the full class
class SurveyBuilder
attr_reader :xml_parser, :sync_survey, :sync_survey_label, :sync_section, :sync_section_label, :sync_question, :sync_question_label, :sync_question_group, :sync_question_group_label, :sync_choice, :sync_choice_label
def initialize
@sync_survey = SurveyBuilder::Sync::Survey.new
@sync_survey_label = SurveyBuilder::Sync::SurveyLabel.new
@sync_section = SurveyBuilder::Sync::Section.new
@sync_section_label = SurveyBuilder::Sync::SectionLabel.new
@sync_question = SurveyBuilder::Sync::Question.new
@sync_question_label = SurveyBuilder::Sync::QuestionLabel.new
@sync_question_group = SurveyBuilder::Sync::QuestionGroup.new
@sync_question_group_label = SurveyBuilder::Sync::QuestionGroupLabel.new
@sync_choice = SurveyBuilder::Sync::Choice.new
@sync_choice_label = SurveyBuilder::Sync::ChoiceLabel.new
@xml_parser = SurveyBuilder::XMLParser.new
end
def call(file, status_id)
xml_document = xml_parser.(file)
language_id = Language.find_by(code: xml_document.xpath('//lang_code').children.first.to_s).id
survey = sync!(xml_document, language_id, status_id, file)
survey
end
private
def sync!(xml_document, language_id, status_id, file)
survey = sync_survey.(xml_document, status_id)
sync_survey_label.(xml_document, survey.id, language_id, file)
xml_document.xpath('//page').each do |xml_section|
section = sync_section.(xml_section, survey.id)
sync_section_label.(xml_section, section.id, language_id)
xml_section.xpath('.//question_group').each do |xml_question_group|
question_group = sync_question_group.(xml_question_group, section.id)
sync_question_group_label.(xml_question_group, question_group.id, language_id)
xml_question_group.xpath('.//question').each do |xml_question|
question = sync_question.(xml_question, question_group.id)
sync_question_label.(xml_question, question.id, language_id)
xml_question.xpath('.//answerchoice').each do |xml_choice|
choice = sync_choice.(xml_choice, question.id)
sync_choice_label.(xml_choice, choice.id, language_id)
end
end
end
end
survey
end
end
Upvotes: 0
Views: 149
Reputation: 1352
My first stab would be something like:
def sync!(xml_document, language_id, status_id, file)
survey = sync_survey.(xml_document, status_id)
sync_survey_label.(xml_document, survey.id, language_id, file)
xml_document.xpath('//page').each do |xml_section|
sync_section(xml_section, ...)
end
survey
end
def sync_section(xml_section, ...)
...
xml_section.xpath('.//question_group').each do |xml_question_group|
sync_question_group(xml_question_group, ...)
end
end
def sync_question_group(xml_question_group, ...)
...
xml_question_group.xpath('.//question').each do |xml_question|
sync_question(xml_question, ...)
end
end
def sync_question(xml_question, ...)
...
xml_question.xpath('.//answerchoice').each do |xml_choice|
sync_choice(xml_choice, ...)
end
end
def sync_choice(xml_choice, ...)
...
end
I elided a fair bit since I am not sure how you defined the existing sync_question_group
which is used in a confusing way with the dot-syntax.
Upvotes: 1