def format_recipe(self):
        """
        Make sure recipe items are in the right place.
        """
        # remove all internal fields.
        for key in RECIPE_REMOVE_FIELDS:
            self.recipe.pop(key, None)

        # make sure default options are not in `options`.
        for key in self.recipe['options'].keys():
            if key in SOUS_CHEF_DEFAULT_OPTIONS.keys():
                self.recipe[key] = self.recipe['options'].pop(key)

        # make sure recipe options are in `options`
        for key in self.recipe.keys():
            if key not in SOUS_CHEF_DEFAULT_OPTIONS.keys() and\
               key not in RECIPE_INTERNAL_FIELDS and\
               key != 'options':

                self.recipe['options'][key] = self.recipe.pop(key)

        # make sure no non-sc fields are in options
        for key in self.recipe['options'].keys():
            if key not in self.sous_chef_opts:
                self.recipe['options'].pop(key, None)
    def format_recipe(self):
        """
        Make sure recipe items are in the right place.
        """
        # remove all internal fields.
        for key in RECIPE_REMOVE_FIELDS:
            self.recipe.pop(key, None)

        # make sure default options are not in `options`.
        for key in self.recipe['options'].keys():
            if key in SOUS_CHEF_DEFAULT_OPTIONS.keys():
                self.recipe[key] = self.recipe['options'].pop(key)

        # make sure recipe options are in `options`
        for key in self.recipe.keys():
            if key not in SOUS_CHEF_DEFAULT_OPTIONS.keys() and\
               key not in RECIPE_INTERNAL_FIELDS and\
               key != 'options':

                self.recipe['options'][key] = self.recipe.pop(key)

        # make sure no non-sc fields are in options
        for key in self.recipe['options'].keys():
            if key not in self.sous_chef_opts:
                self.recipe['options'].pop(key, None)
    def update_sous_chef_defaults(self):
        """
        Merge in sous chef defaults.
        """
        for key in SOUS_CHEF_DEFAULT_OPTIONS.keys():
            # if the key is in the recipe
            # validate it and add in back in.
            if key in self.recipe:
                self.recipe[key] = self.validate_opt(key, top_level=True)

            # otherwise, merge it in.
            else:
                # if the key should be a slug, fall back on the sous chef
                # slug and add a random hash.
                if key == 'slug':
                    slug = "{}-{}".format(self.sous_chef, gen_short_uuid())
                    self.recipe['slug'] = slug

                # inhert the sous chef's name + description
                elif key == 'name':
                    self.recipe[key] = self.sous_chef_name

                # inhert the sous chef's name + description
                elif key == 'description':
                    self.recipe[key] = self.sous_chef_desc

                # fallback on sous chef defaults.
                else:
                    self.recipe[key] = self.validate_opt(key, top_level=True)
    def update_sous_chef_defaults(self):
        """
        Merge in sous chef defaults.
        """
        for key in SOUS_CHEF_DEFAULT_OPTIONS.keys():
            # if the key is in the recipe
            # validate it and add in back in.
            if key in self.recipe:
                self.recipe[key] = self.validate_opt(key, top_level=True)

            # otherwise, merge it in.
            else:
                # if the key should be a slug, fall back on the sous chef
                # slug and add a random hash.
                if key == 'slug':
                    slug = "{}-{}".format(self.sous_chef, gen_short_uuid())
                    self.recipe['slug'] = slug

                # inhert the sous chef's name + description
                elif key == 'name':
                    self.recipe[key] = self.sous_chef_name

                # inhert the sous chef's name + description
                elif key == 'description':
                    self.recipe[key] = self.sous_chef_desc

                # fallback on sous chef defaults.
                else:
                    self.recipe[key] = self.validate_opt(key, top_level=True)
    def validate(self):
        """
        Validate a recipe.
        """
        # format it
        self.format_recipe()

        # merge in sous chef defaults.
        self.update_sous_chef_defaults()

        # validate and parse the options:
        for key in self.sous_chef_opts.keys():
            if key not in SOUS_CHEF_DEFAULT_OPTIONS.keys():
                self.recipe['options'][key] = self.validate_opt(key)

        # validate the schedule
        self.validate_schedule()

        # return the valid recipe
        return copy.copy(self.recipe)
    def validate(self):
        """
        Validate a recipe.
        """
        # format it
        self.format_recipe()

        # merge in sous chef defaults.
        self.update_sous_chef_defaults()

        # validate and parse the options:
        for key in self.sous_chef_opts.keys():
            if key not in SOUS_CHEF_DEFAULT_OPTIONS.keys():
                self.recipe['options'][key] = self.validate_opt(key)

        # validate the schedule
        self.validate_schedule()

        # return the valid recipe
        return copy.copy(self.recipe)