docs: def generate_raml_resources(module, api_root, user):
      """Compile a Pale module's endpoint documentation into RAML format.
  
      RAML calls Pale endpoints 'resources'. This function converts Pale
      endpoints into RAML resource format.
  
      The returned string should be appended to the RAML documentation file
      before it is returned.
      """
  
      from pale import extract_endpoints, extract_resources, is_pale_module
      if not is_pale_module(module):
          raise ValueError(
                  """The passed in `module` (%s) is not a pale module. `paledoc`
                  only works on modules with a `_module_type` set to equal
                  `pale.ImplementationModule`.""")
  
      raml_resources = extract_endpoints(module)
      raml_resource_doc_flat = { ep._route_name: document_endpoint(ep) for ep in raml_resources }
      raml_resource_doc_tree = generate_raml_tree(raml_resource_doc_flat, api_root)
  
      # @TODO generate this dynamically
      pale_argument_type_map = {
          "StringArgument": "string",
          "QueryCursorArgument": "string",
          "KeyArgument": "string",
          "IntegerArgument": "integer",
          "BooleanArgument": "boolean",
          "DateArgument": "date",
          "StringChoiceArgument": "string",
          "QueryKindsArgument": "string",
          "StringChoiceArgument": "string",
          "ListArgument": "array"
      }
  
  
      def check_children_for_public_endpoints(subtree):
          """Recursively check a subtree to see if any of its children require permissions.
          Returns an object with property "public" set to True
          if there are children that do not require permissions, false if not."""
  
          found_children = {}
          found_children["public"] = False
  
          def subroutine(subtree, found_children):
              if subtree.get("path") != None and len(subtree["path"]) > 0:
                  path = subtree["path"]
                  for branch in path:
                      if path[branch].get("endpoint") != None and path[branch]["endpoint"].get("requires_permission") == None:
                          found_children["public"] = True
                      else:
                          subroutine(path[branch], found_children)
  
          subroutine(subtree, found_children)
  
          return found_children
  
  
      def print_resource_tree(tree, output, indent, user, level=0):
          """Walk the tree and add the endpoint documentation to the output buffer.
          The user will only see documentation for endpoints they have the appropriate
          permissions for.
          """
  
          # check for an endpoint at this level first
          if tree.get("endpoint") != None:
  
              this_endpoint = tree["endpoint"]
  
              # check if user has permission to view this endpoint
              # this is currently an on/off switch: if any endpoint has a "@requires_permission"
              # decorator, user.is_admin must be True for the user to see documentation
              # @TODO - make this permission more granular if necessary
              if this_endpoint.get("requires_permission") != None and user != None and user.is_admin or \
                  this_endpoint.get("requires_permission") == None:
  
                  indent += "  "
                  # add the HTTP method
                  if this_endpoint.get("http_method") != None:
                      output.write(indent + this_endpoint["http_method"].lower() + ":\n")
                      indent += "  "
  
                      # add the description
                      if this_endpoint.get("description") != None:
                          modified_description = clean_description(this_endpoint["description"])
                          output.write(indent + "description: " + modified_description + "\n")
  
                      # add queryParameters per RAML spec
                      if this_endpoint.get("arguments") != None and len(this_endpoint["arguments"]) > 0:
                          output.write(indent + "queryParameters:\n")
                          sorted_arguments = OrderedDict(sorted(this_endpoint["arguments"].items(), key=lambda t: t[0]))
                          indent += "  "
  
                          for argument in sorted_arguments:
                              output.write(indent + argument + ":\n")
                              indent += "  "
                              this_argument = sorted_arguments[argument]
  
                              for arg_detail in this_argument:
                                  # check for special kinds of queryParameters
                                  this_arg_detail = this_argument[arg_detail]
  
                                  if this_arg_detail != None:
  
                                      if arg_detail == "default":
                                          default = str(this_arg_detail)
                                          if default == "True" or default == "False":
                                              default = default.lower()
                                          output.write(indent + arg_detail + ": " + default + "\n")
                                      elif arg_detail == "description":
                                          output.write(indent + "description: " + this_arg_detail + "\n")
                                      elif arg_detail == "type":
                                          if pale_argument_type_map.get(this_arg_detail) != None:
                                              if pale_argument_type_map[this_arg_detail] == "array":
                                                  # @TODO set the items dynamically
                                                  output.write(indent + "type: array\n")
                                                  output.write(indent + "items: string\n")
                                              else:
                                                  output.write(indent + "type: " + pale_argument_type_map[this_arg_detail].replace(" ", "_") + "\n")
                                          else:
                                              output.write(indent + "type: " + this_arg_detail.replace(" ", "_") + "\n")
                                      elif arg_detail == "required":
                                          output.write(indent + "required" + ": " + str(this_arg_detail).lower() + "\n")
                                      elif arg_detail == "min_length":
                                          output.write(indent + "minLength: " + str(this_arg_detail) + "\n")
                                      elif arg_detail == "max_length":
                                          output.write(indent + "maxLength: " + str(this_arg_detail) + "\n")
                                      elif arg_detail == "min_value":
                                          output.write(indent + "minimum: " + str(this_arg_detail) + "\n")
                                      elif arg_detail == "max_value":
                                          output.write(indent + "maximum: " + str(this_arg_detail) + "\n")
  
                              indent = indent[:-2]    # reset indent after arg_detail
  
                          indent = indent[:-2]    # reset indent after argument
  
                      # add the responses
                      if this_endpoint.get("returns") != None and len(this_endpoint["returns"]) > 0:
                          output.write(indent + "responses:\n")
                          this_response = this_endpoint["returns"]
                          indent += "  "
  
                          # @TODO refactor the endpoints so that they use a variable called _success
                          # to determine the HTTP code for a successful response
                          # (see changes made to history.py)
  
                          if this_response.get("success") != None:
                              output.write(indent + str(this_response["success"]) + ":\n")
                          else:
                              output.write(indent + "200:\n")
                          indent += "  "
                          output.write(indent + "body:\n")
                          indent += "  "
  
                          for res_detail in this_response:
                              if res_detail != "success":
                                  if res_detail == "resource_type":
                                      output.write(indent + "type: " + this_endpoint["returns"][res_detail].replace(" ", "_") + "\n")
                                  elif res_detail == "description":
                                      modified_description = clean_description(this_endpoint["returns"][res_detail])
                                      output.write(indent + "description: " + modified_description + "\n")
  
                          indent = indent [:-6]   # resent indent after responses
  
                      indent = indent [:-2]   # reset indent after endpoint
  
  
          # check for further branches and recurse on them
          if tree.get("path") != None and len(tree["path"]) > 0:
              # only increase the indent if we are not on root level
              if level > 0:
                  # set the base indentation per the level we are at in the tree
                  indent = level * "  "
  
              subtree = tree["path"]
  
              admin_user = user!= None and user.is_admin != None and user.is_admin
  
              for branch in subtree:
  
                  has_public_children = check_children_for_public_endpoints(subtree[branch]).get("public") == True
  
                  is_public_endpoint = subtree[branch].get("endpoint") != None \
                      and subtree[branch]["endpoint"].get("requires_permission") == None
  
                  # @TODO - make this permission more granular if necessary
                  # if the user is admin or this endpoint is pubic or has public children:
                  if admin_user or has_public_children or is_public_endpoint:
                      # write branch name
                      output.write(indent + "/" + branch + ":\n")
                      # and continue printing the endpoints
                      print_resource_tree(subtree[branch], output, indent, user, level=level+1)
  
  
  
      output = StringIO()
      indent = ""
  
      print_resource_tree(raml_resource_doc_tree, output, indent, user)
      raml_resources = output.getvalue()
      output.close()
      return raml_resources