def included_recipes(ast, options = { with_partial_names: true })
      raise_unless_xpath!(ast)

      filter = ["[count(descendant::args_add) = 1]"]

      # If `:with_partial_names` is false then we won't include the string
      # literal portions of any string that has an embedded expression.
      unless options[:with_partial_names]
        filter << "[count(descendant::string_embexpr) = 0]"
      end

      string_desc = '[descendant::args_add/string_literal]/
        descendant::tstring_content'
      included = ast.xpath([
        "//command[ident/@value = 'include_recipe']",
        "//fcall[ident/@value = 'include_recipe']/
         following-sibling::arg_paren",
      ].map do |recipe_include|
        recipe_include + filter.join + string_desc
      end.join(" | "))

      # Hash keyed by recipe name with matched nodes.
      included.inject(Hash.new([])) { |h, i| h[i["value"]] += [i]; h }
    end
def included_recipes(ast, options = { with_partial_names: true })
      raise_unless_xpath!(ast)

      filter = ["[count(descendant::args_add) = 1]"]

      # If `:with_partial_names` is false then we won't include the string
      # literal portions of any string that has an embedded expression.
      unless options[:with_partial_names]
        filter << "[count(descendant::string_embexpr) = 0]"
      end

      string_desc = '[descendant::args_add/string_literal]/
        descendant::tstring_content'
      included = ast.xpath([
        "//command[ident/@value = 'include_recipe']",
        "//fcall[ident/@value = 'include_recipe']/
         following-sibling::arg_paren",
      ].map do |recipe_include|
        recipe_include + filter.join + string_desc
      end.join(" | "))

      # Hash keyed by recipe name with matched nodes.
      included.inject(Hash.new([])) { |h, i| h[i["value"]] += [i]; h }
    end
Retrieve the recipes that are included within the given recipe AST.

 These two usages are equivalent:

     included_recipes(ast)
     included_recipes(ast, :with_partial_names => true)