def run # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
      self.config = Chef::Config.merge!(config)

      # OPTIMIZE: Maybe instead of a flag, infer the graph format from fileext?

      # Parse the configuration options, provide defaults where needed
      output_format = if config[:graphformat]
                        config[:graphformat].to_sym
                      else
                        :png
                      end
      Chef::Log.debug("Output format is: #{output_format}")

      # Determine of a filename has been passed
      filename = if @name_args.size >= 1
                   @name_args[0]
                 else
                   "role-spaghetti.#{output_format}"
                 end
      Chef::Log.debug("Output filename is: #{filename}")

      loaded_roles = loader.find_all_objects(config[:role_path])

      # If we can't find any roles, it's pointless to continue.
      if loaded_roles.empty?
        ui.fatal("No roles were found in role_path: #{config[:role_path]}")
        ui.fatal('Ensure that your knife.rb has the correct path.')
        exit 1
      end

      g = GraphViz.new(:RoleDependencies, type: :digraph,
                                          fontname: 'Verdana', fontsize: 20,
                                          label: "\n\n\nChef Role Dependencies\n",
                                          rankdir: 'LR',
                                          overlap: 'false',
                                          compound: 'true')

      loaded_roles.each do |role_file|
        # Create an absolute path, since some file references include relative paths
        abspath = File.absolute_path(File.join(config[:role_path], role_file))

        # The object_from_file method figures out the ruby/json logic
        role = loader.object_from_file(abspath)

        Chef::Log.debug("Loaded role is: #{role}")

        # OPTIMIZE: Handle environment run_lists

        g.node[:shape] = 'box'
        g.node[:style] = 'rounded'
        g.node[:color] = 'red'
        role_node = g.add_nodes("#{role.name}_role", label: role.name)

        # This logic is to ensure that an embedded role doesn't change color
        role.run_list.each do |rli|
          if rli.role?
            g.node[:shape] = 'box'
            g.node[:style] = 'rounded'
            g.node[:color] = 'red'
            rli_node = g.add_nodes("#{rli.name}_role", label: rli.name)
          else
            g.node[:shape] = 'component'
            g.node[:color] = 'blue'
            rli_node = g.add_nodes(rli.name)
          end

          g.add_edges(role_node, rli_node)
        end
      end

      # Let's write out the graph to a file
      if config[:neatograph]
        g.output(output_format => filename.to_s, :use => 'neato')
      else
        # default to dot
        g.output(output_format => filename.to_s)
      end

      ui.msg("A Role dependency graph has been written to #{filename}")
    end
def run # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
      self.config = Chef::Config.merge!(config)

      # OPTIMIZE: Maybe instead of a flag, infer the graph format from fileext?

      # Parse the configuration options, provide defaults where needed
      output_format = if config[:graphformat]
                        config[:graphformat].to_sym
                      else
                        :png
                      end
      Chef::Log.debug("Output format is: #{output_format}")

      # Determine of a filename has been passed
      filename = if @name_args.size >= 1
                   @name_args[0]
                 else
                   "role-spaghetti.#{output_format}"
                 end
      Chef::Log.debug("Output filename is: #{filename}")

      loaded_roles = loader.find_all_objects(config[:role_path])

      # If we can't find any roles, it's pointless to continue.
      if loaded_roles.empty?
        ui.fatal("No roles were found in role_path: #{config[:role_path]}")
        ui.fatal('Ensure that your knife.rb has the correct path.')
        exit 1
      end

      g = GraphViz.new(:RoleDependencies, type: :digraph,
                                          fontname: 'Verdana', fontsize: 20,
                                          label: "\n\n\nChef Role Dependencies\n",
                                          rankdir: 'LR',
                                          overlap: 'false',
                                          compound: 'true')

      loaded_roles.each do |role_file|
        # Create an absolute path, since some file references include relative paths
        abspath = File.absolute_path(File.join(config[:role_path], role_file))

        # The object_from_file method figures out the ruby/json logic
        role = loader.object_from_file(abspath)

        Chef::Log.debug("Loaded role is: #{role}")

        # OPTIMIZE: Handle environment run_lists

        g.node[:shape] = 'box'
        g.node[:style] = 'rounded'
        g.node[:color] = 'red'
        role_node = g.add_nodes("#{role.name}_role", label: role.name)

        # This logic is to ensure that an embedded role doesn't change color
        role.run_list.each do |rli|
          if rli.role?
            g.node[:shape] = 'box'
            g.node[:style] = 'rounded'
            g.node[:color] = 'red'
            rli_node = g.add_nodes("#{rli.name}_role", label: rli.name)
          else
            g.node[:shape] = 'component'
            g.node[:color] = 'blue'
            rli_node = g.add_nodes(rli.name)
          end

          g.add_edges(role_node, rli_node)
        end
      end

      # Let's write out the graph to a file
      if config[:neatograph]
        g.output(output_format => filename.to_s, :use => 'neato')
      else
        # default to dot
        g.output(output_format => filename.to_s)
      end

      ui.msg("A Role dependency graph has been written to #{filename}")
    end
TODO: Reduce overall complxity of this method