#!/usr/bin/env python2 ############################################################################## # `dependency` Ansible/Jinja2 filters for `qb/ruby/dependency` role. ############################################################################## # Imports # ============================================================================ # Make Python 2 more Python 3-like from __future__ import (absolute_import, division, print_function) __metaclass__ = type from ansible.errors import AnsibleError # Some imports you may often want: # import sys # improt os # import subprocess # import yaml # improt json # Utilities # ============================================================================ def kwd_args(f): ''' Decorator to convert a single dict arg to keyword args. Allows usage of filter with keyword args as "subject" or whatever you call it: { 'owner': 'beiarea', 'name': 'www-rails_web', 'tag': '0.1.2' } | to_image_id ''' def new_f(*args, **kwds): if len(args) == 1 and isinstance(args[0], dict): return f(**args[0]) else: return f(*args, **kwds) return new_f def quote(string): ''' A dumb and bad sinlge quoert that relies on the argument not having any single quotes in it. :param string: str String to quote :rtype: str ''' return "'{}'".format(string) def gemspec_dep_call(dev): ''' Get the correct 'spec.add_dependency' or 'spec.add_development_dependency' depending on `dev`. :param dev: bool :rtype: str ''' if dev: return 'spec.add_development_dependency' else: return 'spec.add_dependency' # Filter Functions # ============================================================================ # # Suggested practice seems to be to define each filter as a top-level function # then expose them via the `FilterModule#filters` method below. # def gemspec_dep_line(name, version = None, dev = False, indent = ' '): ''' Generate a dependency line for a `.gemspec` file. :param name: The gem name :type name: str :param version: The optional version spec(s) :type version: None | str | list<str> :param dev: If the dependency is development or not :type dev: bool :return: The dependency line for the `.gemspec` file. :rtype: str >>> gemspec_dep_line(name = 'yard') " spec.add_dependency 'yard'" >>> gemspec_dep_line( ... name = 'yard', ... version = '~> 0.9.12', ... ) " spec.add_dependency 'yard', '~> 0.9.12'" >>> gemspec_dep_line( ... name = 'yard', ... version = '~> 0.9.12', ... dev = True, ... ) " spec.add_development_dependency 'yard', '~> 0.9.12'" >>> gemspec_dep_line( ... name = 'bundler', ... version = ['~> 1.16', '>= 1.16.1'], ... dev = True, ... ) " spec.add_development_dependency 'bundler', '~> 1.16', '>= 1.16.1'" ''' call = gemspec_dep_call(dev) args = [quote(name)] if not version is None: if isinstance(version, list): for condition in version: args.append( quote(condition) ) else: args.append(quote(version)) return "{}{} {}".format(indent, call, ", ".join(args)) def gemspec_dep_re_str(name, version = None, dev = False): ''' Generate a regex string for Ansible's `lineinfile` module to match a dependency line for a `.gemspec` file. Params :rtype: str Does not doctest well due to backslashness. ''' call = gemspec_dep_call(dev) return "^\\s+{call}\s+[\\'\\\"]{name}[\\'\\\"]".format( call = call, name = name, ) def gemspec_dep_insert_after(name, version = None, dev = False): ''' Generate a regex string for Ansible's `lineinfile` module to match the last `spec.add_dependency` or `spec.add_development_dependency`, depending on `dev`. Does not doctest well due to backslashness. :rtype: str ''' call = gemspec_dep_call(dev) return "^\\s+{call}".format(call = call) # Module # ============================================================================ # # How Ansible finds the filters. It looks like it gets instantiated with # no arguments, at least most of the time, so it pretty much just serves as # a well-known name to obtain the function references from. # class FilterModule(object): ''' Ansible/Jinja2 filters for `qb/ruby/dependency` role. ''' def filters(self): return { 'to_gemspec_dep_line': kwd_args(gemspec_dep_line), 'to_gemspec_dep_re_str': kwd_args(gemspec_dep_re_str), 'to_gemspec_dep_insert_after': kwd_args(gemspec_dep_insert_after), } # filters() # FilterModule # Testing # ============================================================================ # # This is not standard Ansible-ness - they use `unittest.TestCase` in separate # files - but `doctest` seemed like a really easy way to add and run tests # for these typically simple functions. # if __name__ == '__main__': import doctest doctest.testmod()