Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camels and snakes #52458

Merged
merged 10 commits into from
Apr 25, 2019
43 changes: 43 additions & 0 deletions doc/topics/jinja/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,49 @@ Returns:
.. _`JMESPath language`: http://jmespath.org/
.. _`jmespath`: https://github.com/jmespath/jmespath.py


.. jinja_ref:: to_snake_case

``to_snake_case``
-----------------

.. versionadded:: Neon

Converts a string from camelCase (or CamelCase) to snake_case.

.. code-block:: jinja

Example: {{ camelsWillLoveThis | to_snake_case }}

Returns:

.. code-block:: text

Example: camels_will_love_this


.. jinja_ref:: to_camelcase

``to_camelcase``
----------------

.. versionadded:: Neon

Converts a string from snake_case to camelCase (or UpperCamelCase if so indicated).

.. code-block:: jinja

Example 1: {{ snake_case_for_the_win | to_camelcase }}

Example 2: {{ snake_case_for_the_win | to_camelcase(uppercamel=True) }}

Returns:

.. code-block:: text

Example 1: snakeCaseForTheWin
Example 2: SnakeCaseForTheWin

Networking Filters
------------------

Expand Down
37 changes: 4 additions & 33 deletions salt/modules/testinframod.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import re
import types

from salt.utils.stringutils import camel_to_snake_case, snake_to_camel_case

log = logging.getLogger(__name__)

try:
Expand Down Expand Up @@ -51,38 +53,7 @@ def _get_module(module_name, backend=default_backend):

"""
backend_instance = testinfra.get_backend(backend)
return backend_instance.get_module(_to_pascal_case(module_name))


def _to_pascal_case(snake_case):
"""Convert a snake_case string to its PascalCase equivalent.

:param snake_case: snake_cased string to be converted
:returns: PascalCase string
:rtype: str

"""
space_case = re.sub('_', ' ', snake_case)
wordlist = []
for word in space_case.split():
wordlist.append(word[0].upper())
wordlist.append(word[1:])
return ''.join(wordlist)


def _to_snake_case(pascal_case):
"""Convert a PascalCase string to its snake_case equivalent.

:param pascal_case: PascalCased string to be converted
:returns: snake_case string
:rtype: str

"""
snake_case = re.sub('(^|[a-z])([A-Z])',
lambda match: '{0}_{1}'.format(match.group(1).lower(),
match.group(2).lower()),
pascal_case)
return snake_case.lower().strip('_')
return backend_instance.get_module(snake_to_camel_case(module_name, uppercamel=True))


def _get_method_result(module_, module_instance, method_name, method_arg=None):
Expand Down Expand Up @@ -297,7 +268,7 @@ def _register_functions():
can be called via salt.
"""
try:
modules_ = [_to_snake_case(module_) for module_ in modules.__all__]
modules_ = [camel_to_snake_case(module_) for module_ in modules.__all__]
except AttributeError:
modules_ = [module_ for module_ in modules.modules]

Expand Down
20 changes: 3 additions & 17 deletions salt/states/testinframod.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, print_function

import re
import logging

from salt.utils.stringutils import camel_to_snake_case

log = logging.getLogger(__name__)

try:
Expand Down Expand Up @@ -36,24 +37,9 @@ def _module_function_wrapper(name, **methods):
return _module_function_wrapper


def _to_snake_case(pascal_case):
"""Convert a PascalCase string to its snake_case equivalent.

:param pascal_case: PascalCased string to be converted
:returns: snake_case string
:rtype: str

"""
snake_case = re.sub('(^|[a-z])([A-Z])',
lambda match: '{0}_{1}'.format(match.group(1).lower(),
match.group(2).lower()),
pascal_case)
return snake_case.lower().strip('_')


def _generate_functions():
try:
modules_ = [_to_snake_case(module_) for module_ in modules.__all__]
modules_ = [camel_to_snake_case(module_) for module_ in modules.__all__]
except AttributeError:
modules_ = [module_ for module_ in modules.modules]

Expand Down
36 changes: 36 additions & 0 deletions salt/utils/stringutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,3 +588,39 @@ def get_diff(a, b, *args, **kwargs):
*args, **kwargs
)
)


@jinja_filter('to_snake_case')
def camel_to_snake_case(camel_input):
'''
Converts camelCase (or CamelCase) to snake_case.
From https://codereview.stackexchange.com/questions/185966/functions-to-convert-camelcase-strings-to-snake-case

:param str camel_input: The camelcase or CamelCase string to convert to snake_case

:return str
'''
res = camel_input[0].lower()
for i, letter in enumerate(camel_input[1:], 1):
if letter.isupper():
if camel_input[i-1].islower() or (i != len(camel_input)-1 and camel_input[i+1].islower()):
res += '_'
res += letter.lower()
return res


@jinja_filter('to_camelcase')
def snake_to_camel_case(snake_input, uppercamel=False):
'''
Converts snake_case to camelCase (or CamelCase if uppercamel is ``True``).
Inspired by https://codereview.stackexchange.com/questions/85311/transform-snake-case-to-camelcase

:param str snake_input: The input snake_case string to convert to camelCase
:param bool uppercamel: Whether or not to convert to CamelCase instead

:return str
'''
words = snake_input.split('_')
if uppercamel:
words[0] = words[0].capitalize()
return words[0] + ''.join(word.capitalize() for word in words[1:])
20 changes: 20 additions & 0 deletions tests/unit/utils/test_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,26 @@ def test_sequence(self):
.render(data={'foo': 'bar'})
self.assertEqual(rendered, '1')

def test_camel_to_snake_case(self):
'''
Test the `to_snake_case` Jinja filter.
'''
rendered = render_jinja_tmpl('{{ \'abcdEfghhIjkLmnoP\' | to_snake_case }}',
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'abcd_efghh_ijk_lmno_p')

def test_snake_to_camel_case(self):
'''
Test the `to_camelcase` Jinja filter.
'''
rendered = render_jinja_tmpl('{{ \'the_fox_jumped_over_the_lazy_dog\' | to_camelcase }}',
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'theFoxJumpedOverTheLazyDog')

rendered = render_jinja_tmpl('{{ \'the_fox_jumped_over_the_lazy_dog\' | to_camelcase(uppercamel=True) }}',
dict(opts=self.local_opts, saltenv='test', salt=self.local_salt))
self.assertEqual(rendered, 'TheFoxJumpedOverTheLazyDog')

def test_is_ip(self):
'''
Test the `is_ip` Jinja filter.
Expand Down