Repository: cookiecutter/cookiecutter

Issue:
Boolean parameters parsed as String
* Cookiecutter version: 2.0
* Template project url: https://github.com/cookiecutter/cookiecutter-django
* Python version: 3.8
* Operating System: Linux

### Description:

We want to be able to ask the user for true/false questions.
The example template shows the current usage of boolean parameters, in {{cookiecutter.project_slug}}/.github/dependabot.yml:
`{%- if cookiecutter.use_docker == 'y' %}` 
This usage is rather problematic, as it is requires the user to enter exactly 'y'. If the user enters "yes" or "true" it won't be valid.

We want to add the ability to specify boolean parameters as "true" JSON booleans and naturally prompt for them.
Thus the usage of booleans in the template's cookiecutter.json would look like:
`"use_docker": false`
Instead of the current usage, which is:
 `"use_docker": "n"`

Currently this example boolean which is specified in the cookiecutter.json file is parsed as a string and thus checking for it would require a string comparison, which raises and exact same problem as the user would have to explicitly enter "true" or "false" instead of a boolean.

This would also simplify the usage of the boolean parameters in files, so the above shown dependabot.yml would change to:
`{%- if cookiecutter.use_docker %}` 


Discussion:

Resolving patch:
diff --git a/cookiecutter/prompt.py b/cookiecutter/prompt.py
--- a/cookiecutter/prompt.py
+++ b/cookiecutter/prompt.py
@@ -143,8 +143,8 @@ def render_variable(env, raw, cookiecutter_dict):
         being populated with variables.
     :return: The rendered value for the default variable.
     """
-    if raw is None:
-        return None
+    if raw is None or isinstance(raw, bool):
+        return raw
     elif isinstance(raw, dict):
         return {
             render_variable(env, k, cookiecutter_dict): render_variable(
@@ -201,6 +201,14 @@ def prompt_for_config(context, no_input=False):
                     cookiecutter_dict, env, key, raw, no_input
                 )
                 cookiecutter_dict[key] = val
+            elif isinstance(raw, bool):
+                # We are dealing with a boolean variable
+                if no_input:
+                    cookiecutter_dict[key] = render_variable(
+                        env, raw, cookiecutter_dict
+                    )
+                else:
+                    cookiecutter_dict[key] = read_user_yes_no(key, raw)
             elif not isinstance(raw, dict):
                 # We are dealing with a regular variable
                 val = render_variable(env, raw, cookiecutter_dict)


Test patch:
diff --git a/tests/test_prompt.py b/tests/test_prompt.py
--- a/tests/test_prompt.py
+++ b/tests/test_prompt.py
@@ -21,7 +21,7 @@ class TestRenderVariable:
         'raw_var, rendered_var',
         [
             (1, '1'),
-            (True, 'True'),
+            (True, True),
             ('foo', 'foo'),
             ('{{cookiecutter.project}}', 'foobar'),
             (None, None),
@@ -39,7 +39,7 @@ def test_convert_to_str(self, mocker, raw_var, rendered_var):
         assert result == rendered_var
 
         # Make sure that non None non str variables are converted beforehand
-        if raw_var is not None:
+        if raw_var is not None and not isinstance(raw_var, bool):
             if not isinstance(raw_var, str):
                 raw_var = str(raw_var)
             from_string.assert_called_once_with(raw_var)
@@ -49,10 +49,10 @@ def test_convert_to_str(self, mocker, raw_var, rendered_var):
     @pytest.mark.parametrize(
         'raw_var, rendered_var',
         [
-            ({1: True, 'foo': False}, {'1': 'True', 'foo': 'False'}),
+            ({1: True, 'foo': False}, {'1': True, 'foo': False}),
             (
                 {'{{cookiecutter.project}}': ['foo', 1], 'bar': False},
-                {'foobar': ['foo', '1'], 'bar': 'False'},
+                {'foobar': ['foo', '1'], 'bar': False},
             ),
             (['foo', '{{cookiecutter.project}}', None], ['foo', 'foobar', None]),
         ],
@@ -380,6 +380,42 @@ def test_should_read_user_choice(self, mocker, choices, context):
         assert expected_choice == actual_choice
 
 
+class TestReadUserYesNo(object):
+    """Class to unite boolean prompt related tests."""
+
+    @pytest.mark.parametrize(
+        'run_as_docker',
+        (
+            True,
+            False,
+        ),
+    )
+    def test_should_invoke_read_user_yes_no(self, mocker, run_as_docker):
+        """Verify correct function called for boolean variables."""
+        read_user_yes_no = mocker.patch('cookiecutter.prompt.read_user_yes_no')
+        read_user_yes_no.return_value = run_as_docker
+
+        read_user_variable = mocker.patch('cookiecutter.prompt.read_user_variable')
+
+        context = {'cookiecutter': {'run_as_docker': run_as_docker}}
+
+        cookiecutter_dict = prompt.prompt_for_config(context)
+
+        assert not read_user_variable.called
+        read_user_yes_no.assert_called_once_with('run_as_docker', run_as_docker)
+        assert cookiecutter_dict == {'run_as_docker': run_as_docker}
+
+    def test_boolean_parameter_no_input(self):
+        """Verify boolean parameter sent to prompt for config with no input."""
+        context = {
+            'cookiecutter': {
+                'run_as_docker': True,
+            }
+        }
+        cookiecutter_dict = prompt.prompt_for_config(context, no_input=True)
+        assert cookiecutter_dict == context['cookiecutter']
+
+
 @pytest.mark.parametrize(
     'context',
     (