Прежде всего вам нужно знать, что такое условие ветви назначения, прежде чем мы сможем решить эту проблему:
При просмотре файла default config мы видим, что максимальное значение по умолчанию равно 15, и это ссылки на http://c2.com/cgi/wiki?AbcMetric и https://en.wikipedia.org/wiki/ABC_Software_Metric.
Давайте сначала посмотрим на ссылки. Первая ссылка гласит следующее:
- Назначение - явный перенос данных в переменную, например, = * = / =% = + = << = >> = & = | = ^ = >>> = ++ -
- Ветвь - явная прямая ветвь программы вне области видимости - вызов функции, вызов метода класса или новый оператор
- Условие - логический / логический тест, ==! = <=> = <> в противном случае по умолчанию try catch? и унарные условные выражения.
Скалярное значение размера AB C (или «совокупная величина») вычисляется как:
|ABC| = sqrt((A*A)+(B*B)+(C*C))
Суммировано: Количество назначений увеличивается, когда Вы назначаете что-то переменной. Количество ветвей увеличивается, когда вы вызываете метод или функцию. Количество условий увеличивается при выполнении сравнений, таких как 1 > 2
.
. Применяя это к вашему коду, мы видим, что это приводит к следующему числу:
def install_package
node.run_state['fullList'].each do |p|
# ^B ^B ^B ^B
rpm_package p do
# ^B
source "#{node.run_state['repo']}/#{p}"
# ^B ^B ^B ^B
options "--prefix #{node['sw']['swclient']['install_path']}/#{node.run_state['sw_inst_path']}"
# ^B ^B ^B ^B ^B ^B ^B ^B
action :install
# ^B
only_if { node.run_state['failureCount'].zero? }
# ^B ^B ^B ^B ^B
end
end
end
В результате получается размер AB C из:
Math.sqrt(0**2 + 23**2 + 0**2) #=> 23
Позвольте мне начать с того, что стандартный максимальный размер AB C довольно низок. И вы можете захотеть сделать это в вашем RuboCop config или отключить настройку все вместе. Теперь давайте посмотрим, как решить эту проблему.
Большую часть времени, когда размер AB C является проблемой, это означает, что у вас слишком большая сложность в вашем методе. Самый простой способ решить эту проблему - создать вспомогательные методы и извлечь сложность из основного метода.
Другой вариант - присвоить значения метода, возвращаемые переменной. таким образом вы кешируете возвращаемое значение. Например, вы вызываете метод node
5 раз. Вы можете сохранить результат метода в переменной и использовать его вместо этого. node = self.node
Таким образом, вы заменяете 5 ветвей на 1 ветвь и 1 присваивание.
Я разработал пример решения, которое извлекает сложность из метода install_package
, в этом ответе предполагается, что код помещен в класс или контекст модуля (в противном случае private
не имеет смысла).
def install_packages # ABC-size Math.sqrt(0**2 + 12**2 + 1**2) #=> 12.041...
node.run_state['fullList'].each do |package|
# ^B ^B ^B ^B
rpm_package package do
# ^B
source package_source(package)
# ^B ^B
options "--prefix #{install_path}"
# ^B ^B
action :install
# ^B
only_if { !failures? }
# ^B ^C ^B
end
end
end
private
def package_source(package) # ABC-size Math.sqrt(0**2 + 3**2 + 0**2) #=> 3
"#{node.run_state['repo']}/#{package}"
# ^B ^B ^B
end
def install_path # ABC-size Math.sqrt(2**2 + 7**2 + 0**2) #=> 7.280...
root_install_path = node['sw']['swclient']['install_path']
# ^A ^B ^B ^B ^B
relative_install_path = node.run_state['sw_inst_path']
# ^A ^B ^B ^B
"#{root_install_path}/#{relative_install_path}"
end
def failures? # ABC-size Math.sqrt(0**2 + 4**2 + 0**2) #=> 4
node.run_state['failureCount'].positive?
# ^B ^B ^B ^B
end
Примечание: Я не совсем уверен, что восклицательный знак в !value
относится к ветвям или условия. Ради примеров я посчитал их условными.