rndr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 703f888719f6993503d82d3786fc7a3eb5c82952
4
+ data.tar.gz: c0d2b4e7a30c6cf7934f4932ff9d9c5b6a9bde11
5
+ SHA512:
6
+ metadata.gz: cb3c39ca03f967924002b91fa64073524dac34084a1b8f2b76b7105beb4c532a73f71ac3dc2b38509642bc791887233004100f302fb83ed0d16a5fc8b60d382a
7
+ data.tar.gz: b069bf6c60fc6bf2ccc1646387a2d38a75ff325a96173c7445f021c684f630325eba1f21c2f89b4d82742034f587eeddf28ca5794cde95ab01412f7bb2fe2180
@@ -0,0 +1,22 @@
1
+ ._*
2
+ .DS_Store
3
+ .*.sw?
4
+ *~
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /.yardoc
9
+ /_yardoc
10
+ /doc/
11
+ /coverage/
12
+ /InstalledFiles
13
+ /pkg/
14
+ /spec/reports/
15
+ /spec/examples.txt
16
+ /test/templates/*.txt
17
+ /test/tmp/
18
+ /test/version_tmp/
19
+ /tmp/
20
+ Gemfile.lock
21
+ .ruby-version
22
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+
4
+ Metrics/AbcSize:
5
+ Max: 20
6
+
7
+ Metrics/ClassLength:
8
+ Enabled: false
9
+
10
+ Metrics/LineLength:
11
+ Max: 100
12
+
13
+ Style/Documentation:
14
+ Enabled: false
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ env:
5
+ - CI=true
6
+
7
+ deploy:
8
+ provider: rubygems
9
+ on:
10
+ tags: true
11
+ api_key:
12
+ secure: wJ2eSj/CABOHR/jGrYs2liXkyccpIVdu3jiHqpqWcrM4AnOgLoglrv3kpKriizfr/Wi2u9GwltQzdZ0SJQkKbsjmLVBzH/fzPJKiDtndDpUGthUpYGIr3F+qzvmwWKxnpMw1zbXRq37u1v2OPr0PEIcicmvWzi4eM1BQNVYnRLL7MlaQwCOvP4GcCnneH7NVqxB8oPEMoXBKPIln7+T505yEOEUk1mgvzbu0y/lwJpKGGaLmNSiSSeM0x0uC0EgCdh05r9lOS9RtwuC2h0tbfJqaRuIsL+TRPfaWtlrkuqMcJrsbRNb8p2ZL0oh994bdfl8YegC4DbEs4bbu5NrZIETsKBP24QKpgokVAJW8+mqeb7GXChtqScoMO62wZN/5K65kbUNxm81T9NjgyQ5wQvLrU6vH5uzcasZcxJhfVXer3SXtH1RsenjulIM9kUqCU1a8ICfTerTvhKi4rIXcMe9tYHeytdq9E6Wip+mrL/JJHBjxjUtUpkTNoLxLIASF/JVjtla5fo6hVkaw4mAQO1ACisWGLEFdF56Hg98pJ9RrVtmf8PvZPgBuTQN+jq3+nzjLhK/mRxtcW2JXqwSeinqtBuKQHESdnJnIuAyazYlpZD9Xsh39SjRR2xYARipq+HNwNxt9u2AHWWp8/UWqNJxwY4Usp+fi6Li7KBG3AQ8=
@@ -0,0 +1,2 @@
1
+ --readme README.md
2
+ --markup markdown
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
@@ -0,0 +1,108 @@
1
+ # Rndr
2
+ [![Build Status](https://travis-ci.org/arc-ts/rndr.svg?branch=master)](https://travis-ci.org/arc-ts/rndr)
3
+ [![Coverage Status](https://coveralls.io/repos/github/arc-ts/rndr/badge.svg?branch=master)](https://coveralls.io/github/arc-ts/rndr?branch=master)
4
+
5
+ Rndr is a small Ruby cli gem that provides a method of rendering discovered erb templates. Variables for use in template rendering can be used directly by providing a either a `yaml` or `json` file, or alternatively a directory may be supplied along with the variable merging strategy. Rndr supports both standard hash merges or recursive (default).
6
+
7
+ Paths can be ignored by either creating a `.rndrignore` file within the current working directly, or supplying a path to a desired ignore file.
8
+
9
+
10
+ * [Commands](#commands)
11
+ * [Check](#check)
12
+ * [List](#list)
13
+ * [Render](#render)
14
+ * [Vars](#vars)
15
+
16
+ ## Commands
17
+
18
+ ```
19
+ $ bin/rndr help
20
+ Commands:
21
+ rndr check # Verifies discovered erb templates.
22
+ rndr help [COMMAND] # Describe available commands or one specific command
23
+ rndr list # List discovered templates.
24
+ rndr render # Renders discovered templates.
25
+ rndr vars # Lists Combined Variables.
26
+ rndr version # Prints rndr Version Information.
27
+ ...
28
+ ```
29
+
30
+ ### Check
31
+
32
+ ```
33
+ $ bin/rndr help check
34
+ Usage:
35
+ rndr check
36
+
37
+ Options:
38
+ e, [--extension=EXTENSION] # Extension of templates.
39
+ # Default: erb
40
+ i, [--ignore=IGNORE] # Path to file containing list of files to be ignored.
41
+ # Default: /Users/demo/ruby/rndr/.rndrignore
42
+ m, [--merge], [--no-merge] # Recursively merge variables instead of replacing.
43
+ # Default: true
44
+ t, [--template=TEMPLATE] # Path to erb template or directory.
45
+ # Default: /Users/demo/ruby/rndr/rndr
46
+ V, [--vars=VARS] # Path to var file or directory.
47
+ # Default: /Users/demo/ruby/rndr/vars
48
+
49
+ Verifies discovered erb templates.
50
+ ```
51
+
52
+ ### List
53
+
54
+ ```
55
+ $ bin/rndr help list
56
+ Usage:
57
+ rndr list
58
+
59
+ Options:
60
+ e, [--extension=EXTENSION] # Extension of templates.
61
+ # Default: erb
62
+ i, [--ignore=IGNORE] # Path to file containing list of files to be ignored.
63
+ # Default: /Users/demo/ruby/rndr/.rndrignore
64
+ t, [--template=TEMPLATE] # Path to erb template or directory.
65
+ # Default: /Users/demo/ruby/rndr/rndr
66
+
67
+ List discovered templates.
68
+ ```
69
+
70
+ ### Render
71
+
72
+ ```
73
+ $ bin/rndr help render
74
+ Usage:
75
+ rndr render
76
+
77
+ Options:
78
+ e, [--extension=EXTENSION] # Extension of templates.
79
+ # Default: erb
80
+ i, [--ignore=IGNORE] # Path to file containing list of files to be ignored.
81
+ # Default: /Users/demo/ruby/rndr/.rndrignore
82
+ m, [--merge], [--no-merge] # Recursively merge variables instead of replacing.
83
+ # Default: true
84
+ t, [--template=TEMPLATE] # Path to erb template or directory.
85
+ # Default: /Users/demo/ruby/rndr/rndr
86
+ V, [--vars=VARS] # Path to var file or directory.
87
+ # Default: /Users/demo/ruby/rndr/vars
88
+
89
+ Renders discovered templates.
90
+ ```
91
+
92
+ ### Vars
93
+
94
+ ```
95
+ $ bin/rndr help vars
96
+ Usage:
97
+ rndr vars
98
+
99
+ Options:
100
+ f, [--format=FORMAT] # Output Format [yaml|json]
101
+ # Default: yaml
102
+ m, [--merge], [--no-merge] # Recursively merge variables instead of replacing.
103
+ # Default: true
104
+ V, [--vars=VARS] # Path to var file or directory.
105
+ # Default: /Users/demo/ruby/rndr/vars
106
+
107
+ Lists Combined Variables.
108
+ ```
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'coveralls/rake/task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ Coveralls::RakeTask.new
7
+
8
+ task :default => :spec
9
+ task :coveralls => [ :spec, :features, 'coveralls:push' ]
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+ # frozen_string_literal: true
4
+
5
+ Signal.trap('INT') { exit 1 }
6
+
7
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib')
8
+ require 'rndr'
9
+
10
+ Rndr::CLI.start(ARGV)
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ # Rndr base module
5
+ # @author Bob Killen <rkillen@umich.edu>
6
+
7
+ module Rndr
8
+ require 'rndr/cli'
9
+ require 'rndr/template'
10
+ require 'rndr/util'
11
+ require 'rndr/version'
12
+ end
@@ -0,0 +1,125 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ require 'thor'
4
+
5
+ module Rndr
6
+ # Parent CLI controls for Rndr.
7
+ # @author Bob Killen <rkillen@umich.edu>
8
+ class CLI < Thor
9
+ desc 'check', 'Verifies discovered erb templates.'
10
+ method_option :extension,
11
+ aliases: :e, type: :string, default: 'erb',
12
+ desc: 'Extension of templates.'
13
+ method_option :ignore,
14
+ aliases: :i, type: :string, default: File.join(Dir.pwd, '.rndrignore'),
15
+ desc: 'Path to file containing list of files to be ignored.'
16
+ method_option :merge,
17
+ aliases: :m, type: :boolean, default: true,
18
+ desc: 'Recursively merge variables instead of replacing.'
19
+ method_option :template,
20
+ aliases: :t, type: :string, default: Dir.pwd,
21
+ desc: 'Path to erb template or directory.'
22
+ method_option :vars,
23
+ aliases: :V, type: :string, default: File.join(Dir.pwd, 'vars'),
24
+ desc: 'Path to var file or directory.'
25
+ def check
26
+ results = Rndr.matches(path: options[:template], ext: options[:extension],
27
+ ignore_path: options[:ignore])
28
+ template_vars = Rndr.read_vars(path: options[:vars], merge: options[:merge])
29
+ results.each do |path|
30
+ template = Template.new(path: path, vars: template_vars)
31
+ print_check_result(path: path, result: template.render?)
32
+ end
33
+ end
34
+
35
+ desc 'list', 'List discovered templates.'
36
+ method_option :extension,
37
+ aliases: :e, type: :string, default: 'erb',
38
+ desc: 'Extension of templates.'
39
+ method_option :ignore,
40
+ aliases: :i, type: :string, default: File.join(Dir.pwd, '.rndrignore'),
41
+ desc: 'Path to file containing list of files to be ignored.'
42
+ method_option :template,
43
+ aliases: :t, type: :string, default: Dir.pwd,
44
+ desc: 'Path to erb template or directory.'
45
+ def list
46
+ results = Rndr.matches(path: options[:template], ext: options[:extension],
47
+ ignore_path: '.rndrignore')
48
+ if results.empty?
49
+ puts 'No matching results.'
50
+ else
51
+ puts results
52
+ end
53
+ end
54
+
55
+ desc 'render', 'Renders discovered templates.'
56
+ method_option :extension,
57
+ aliases: :e, type: :string, default: 'erb',
58
+ desc: 'Extension of templates.'
59
+ method_option :ignore,
60
+ aliases: :i, type: :string, default: File.join(Dir.pwd, '.rndrignore'),
61
+ desc: 'Path to file containing list of files to be ignored.'
62
+ method_option :merge,
63
+ aliases: :m, type: :boolean, default: true,
64
+ desc: 'Recursively merge variables instead of replacing.'
65
+ method_option :template,
66
+ aliases: :t, type: :string, default: Dir.pwd,
67
+ desc: 'Path to erb template or directory.'
68
+ method_option :vars,
69
+ aliases: :V, type: :string, default: File.join(Dir.pwd, 'vars'),
70
+ desc: 'Path to var file or directory.'
71
+ def render
72
+ results = Rndr.matches(path: options[:template], ext: options[:extension],
73
+ ignore_path: '.rndrignore')
74
+ template_vars = Rndr.read_vars(path: options[:vars], merge: options[:merge])
75
+ results.each do |path|
76
+ template = Template.new(path: path, vars: template_vars)
77
+ render_path = path.gsub(/.#{options[:extension]}$/, '')
78
+ template.render(render_path) if template.render?
79
+ print_check_result(path: render_path, result: template.render?)
80
+ end
81
+ end
82
+
83
+ desc 'vars', 'Lists Combined Variables.'
84
+ method_option :format,
85
+ aliases: :f, type: :string, default: 'yaml',
86
+ desc: 'Output Format [yaml|json]'
87
+ method_option :merge,
88
+ aliases: :m, type: :boolean, default: true,
89
+ desc: 'Recursively merge variables instead of replacing.'
90
+ method_option :vars,
91
+ aliases: :V, type: :string, default: File.join(Dir.pwd, 'vars'),
92
+ desc: 'Path to var file or directory.'
93
+ def vars
94
+ result = Rndr.read_vars(path: options[:vars], merge: options[:merge])
95
+ case options[:format].downcase
96
+ when 'json'
97
+ puts result.to_json
98
+ when 'yaml'
99
+ puts result.to_yaml
100
+ else
101
+ puts 'Invalid Format.'
102
+ end
103
+ end
104
+
105
+ desc 'version', 'Prints rndr Version Information.'
106
+ map %w(-v --version) => :version
107
+ def version
108
+ puts "Rndr Version: #{Rndr::VERSION}"
109
+ end
110
+
111
+ private
112
+
113
+ # print_check_result puts the results of an attempted Template.render? action.
114
+ # @param path [String] Path to template or rendered template.
115
+ # @param result [Boolean] True is successfully rendered template.
116
+ def print_check_result(path:, result:)
117
+ case result
118
+ when true
119
+ puts "#{path} [OK]"
120
+ when false
121
+ puts "#{path} [FAIL]"
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ require 'erb'
4
+
5
+ module Rndr
6
+ # This class is used to handle the rendering of `ERB` templates.
7
+ # @author Bob Killen <rkillen@umich.edu>
8
+ class Template
9
+ # Constructs a new instance of Template.
10
+ # @param path [String] The path to the template file.
11
+ # @param vars [Hash] A hash of the variables to be passed to the template's binding.
12
+ def initialize(path:, vars: {})
13
+ @path = File.absolute_path(path)
14
+ @vars = vars
15
+ end
16
+
17
+ # Verifies if a template is renderable or not.
18
+ # @return [Boolean] True if template is renderable.
19
+ def render?
20
+ render_helper
21
+ end
22
+
23
+ # Renders the template to the supplied path.
24
+ # @param render_path [String] Path to desired rendered template location.
25
+ # @return [Boolean] True if template was rendered successfully.
26
+ def render(render_path)
27
+ render_helper(render_path)
28
+ end
29
+
30
+ private
31
+
32
+ # Does the heavy lifting of template generation.
33
+ # If no path is supplied, it simply tests if the template is renderable.
34
+ # @param render_path [String] Path to desired rendered template location.
35
+ # @return [Boolean] True if template is renderable, and rendered file written.
36
+ def render_helper(render_path = nil)
37
+ b = binding
38
+ @vars.each { |k, v| b.local_variable_set(k, v) }
39
+ rendered = ERB.new(File.read(@path)).result(b)
40
+ unless render_path.nil?
41
+ File.open(render_path, 'w') { |f| f.write(rendered) }
42
+ end
43
+ true
44
+ rescue
45
+ false
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,147 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ require 'deep_merge'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ module Rndr
8
+ # Accepts a path (either file or directory), a file extension, and the name
9
+ # of a file within the working directory that contains a list of file glob
10
+ # patterns to be skipped. It returns an array of filtered matched paths.
11
+ # @param path [String] The path to the template file or directory.
12
+ # @param ext [String] The File name extension to identify template files.
13
+ # @param ignore_path [String] The path to ignore file.
14
+ # @return [Array<String>] list of paths to matching results.
15
+ def self.matches(path:, ext: 'erb', ignore_path: File.join(Dir.pwd, '.rndrignore'))
16
+ matched_paths = match_files(path: File.absolute_path(path), ext: ext)
17
+ ignore_file_path = File.absolute_path(ignore_path)
18
+ if !matched_paths.empty? && File.exist?(ignore_file_path)
19
+ return filter_ignored_paths(path_list: matched_paths, ignore_path: ignore_file_path)
20
+ else
21
+ return matched_paths
22
+ end
23
+ end
24
+
25
+ # Accepts a file or directory and will attempt to parse the discovered file(s) as either json
26
+ # or yaml. It will then be returned as a hash intended for use with seeding the binding used for
27
+ # template rendering in {Template#render}.
28
+ # @param path [String] The path to the vars file or directory. Will process both json and yaml.
29
+ # @param merge [Boolean] True to enable recursive merge of hashes.
30
+ # @return [Hash] A hash containing the processed variables. Will be used to send to the binding
31
+ # of a rendered template. See {Template#render} for more information.
32
+ def self.read_vars(path:, merge: true)
33
+ vars_path = File.absolute_path(path)
34
+ return read_vars_file(vars_path) if File.file?(vars_path)
35
+ return read_vars_dir(path: vars_path, merge: merge) if File.directory?(vars_path)
36
+ {}
37
+ end
38
+
39
+ class << self
40
+ private
41
+
42
+ # @param path [String] Path to file or directory to test match condition.
43
+ # @param ext [String] The extension to use as the match condition.
44
+ # @return [Array<String>] Array of paths to matching files.
45
+ def match_files(path:, ext:)
46
+ return [path] if File.file?(path) && File.fnmatch("*#{ext}", path)
47
+ if File.directory?(path)
48
+ matched_paths = Dir["#{path}/**/*.#{ext}"]
49
+ matched_paths.reject! { |file| File.directory?(file) }
50
+ return matched_paths
51
+ end
52
+ []
53
+ end
54
+
55
+ # filter_ignored_paths removes paths from the supplied list if they match a
56
+ # path within the supplied ignore_path.
57
+ # @param path_list [Array<String>] Array of paths to matching files before prune
58
+ # from ignore_path.
59
+ # @param ignore_path [String] Path to ignore file. Passed to @see #ignore_paths for
60
+ # processing.
61
+ # @return [Array<String>] Pruned list of paths with files matching the glob patterns
62
+ # in ignore_path removed.
63
+ def filter_ignored_paths(path_list:, ignore_path:)
64
+ ignored_paths = ignore_paths(ignore_path)
65
+ path_list.reject do |file|
66
+ ignored_paths.any? do |ignore|
67
+ File.fnmatch(ignore, file, File::FNM_PATHNAME)
68
+ end
69
+ end
70
+ end
71
+
72
+ # ignore_paths returns a list of paths used to filter possible discovered tempaltes
73
+ # that should be ignored.
74
+ # @param ignore_path [String] Path to ignore file.
75
+ # @return [Array<String>] Generated list of paths to be ignored.
76
+ def ignore_paths(ignore_path)
77
+ ignore_list = File.readlines(ignore_path)
78
+ ignore_list.map! { |line| line.chomp.strip }
79
+ ignore_list.reject! { |line| line.empty? || line =~ /^(#|!)/ }
80
+ ignore_list.map! { |line| File.absolute_path(line, File.dirname(ignore_path)) }
81
+ ignore_list
82
+ end
83
+
84
+ # read_vars_file reads the data stored in file as supplied by path. processed_vars
85
+ # json or yaml
86
+ # @param path [String] Path to file with variables to be processed.
87
+ # @return [Hash, nil] Hash of processed data read from var passed var file.
88
+ def read_vars_file(path)
89
+ data = File.read(path)
90
+ return JSON.load(data) if json?(data)
91
+ return YAML.load(data) if yaml?(data)
92
+ nil
93
+ end
94
+
95
+ # read_vars_dir combines all the variables within a supplied directory. It can injest
96
+ # json or yaml.
97
+ # @param path [String] Path to directory containing files to be ingested and vars
98
+ # processed.
99
+ # @param merge [Boolean] True if variables from files should be recursively merged.
100
+ # @return [Hash] Hash of processed variables read from files within the passed.
101
+ def read_vars_dir(path:, merge:)
102
+ processed_vars = {}
103
+ Dir[File.join(path, '*')].each do |file|
104
+ file_vars = read_vars_file(file)
105
+ unless file_vars.nil?
106
+ processed_vars =
107
+ merge_vars(base_hash: processed_vars, hash: file_vars, merge: merge)
108
+ end
109
+ end
110
+ processed_vars
111
+ end
112
+
113
+ # json? tests if the supplied json is parsable or not.
114
+ # @param data [String] String containing read data from file.
115
+ # @return [Boolean] True if data is valid json.
116
+ def json?(data)
117
+ JSON.parse(data)
118
+ true
119
+ rescue
120
+ false
121
+ end
122
+
123
+ # yaml? tests if the supplied yaml is parsable or not.
124
+ # @param data [String] String containing read data from file.
125
+ # @return [Boolean] True if data is valid yaml.
126
+ def yaml?(data)
127
+ YAML.load(data)
128
+ true
129
+ rescue
130
+ false
131
+ end
132
+
133
+ # merge_vars handles merging hashes, either with a recursive merge (default) or with
134
+ # a standard merge, which has a 'replace' effect.
135
+ # @param base_hash [Hash] Original Hash to have values merged into.
136
+ # @param hash [Hash] Secondary hash to be merged into base hash.
137
+ # @param merge [Boolean] True to enable recursive merging of hashes.
138
+ # @return [Hash] The merged hash.
139
+ def merge_vars(base_hash:, hash:, merge: true)
140
+ if base_hash.is_a?(Hash) && hash.is_a?(Hash)
141
+ return base_hash.deep_merge!(hash) if merge == true
142
+ return base_hash.merge!(hash) if merge == false
143
+ end
144
+ {}
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,8 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Rndr
5
+ VERSION = File.read(
6
+ File.expand_path('../../../version.txt', __FILE__)
7
+ ).chomp
8
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'rndr/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = 'rndr'
9
+ gem.version = Rndr::VERSION
10
+ gem.authors = ['Bob Killen']
11
+ gem.homepage = 'https://github.com/arc-ts/rndr'
12
+ gem.email = ['arcts-dev@umich.edu']
13
+ gem.license = 'MIT'
14
+ gem.summary = 'Render Ruby templates quickly and easily.'
15
+ gem.description = 'rndr is a tool that simply renders Ruby erb ' \
16
+ 'templates for use with other applications.'
17
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
+ gem.require_paths = ['lib']
20
+
21
+ gem.required_ruby_version = '>= 2.3'
22
+
23
+ gem.add_dependency 'deep_merge', '>= 1.1.1'
24
+ gem.add_dependency 'thor', '>= 0.19.1'
25
+
26
+ gem.add_development_dependency 'bundler', '>= 1.12.5'
27
+ gem.add_development_dependency 'rake', '>= 11.2.2'
28
+ gem.add_development_dependency 'rspec', '>= 3.5.0'
29
+ gem.add_development_dependency 'coveralls', '>= 0.8.15'
30
+ end
@@ -0,0 +1,172 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'fileutils'
5
+ require 'rndr'
6
+ require_relative 'spec_helper'
7
+
8
+ module Rndr # rubocop:disable Metris/ModuleLength
9
+ RSpec.describe CLI do
10
+ before(:all) { Dir.chdir(File.join(Dir.pwd, 'test')) }
11
+ after(:all) { Dir.chdir('../') }
12
+
13
+ let(:cli) { CLI.new }
14
+
15
+ describe '#check' do
16
+ context 'When the default options are supplied' do
17
+ before do
18
+ cli.options = {
19
+ extension: 'erb', ignore: '.rndrignore',
20
+ merge: true, template: 'templates', vars: 'vars'
21
+ }
22
+ end
23
+ it 'should list the correctly matched results' do
24
+ expect { cli.check }.to output(/rendertest.txt.erb \[OK\]$/).to_stdout
25
+ expect { cli.check }.to_not output(/ignoretest.txt.erb [OK]$/).to_stdout
26
+ end
27
+ end
28
+
29
+ context 'When an alternate exntesion is provided' do
30
+ before do
31
+ cli.options = {
32
+ extension: 'tmplt', ignore: '.rndrignore',
33
+ merge: true, template: 'templates', vars: 'vars'
34
+ }
35
+ end
36
+ it 'should list the correctly matched results' do
37
+ expect { cli.check }.to output(/exttest.txt.tmplt \[OK\]$/).to_stdout
38
+ expect { cli.check }.to_not output(/.erb/).to_stdout
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#list' do
44
+ context 'When the default options are supplied' do
45
+ before { cli.options = { extension: 'erb', ignore: '.rndrignore', template: 'templates' } }
46
+ it 'should list the correctly matched results' do
47
+ expect { cli.list }.to output(/rendertest.txt.erb$/).to_stdout
48
+ expect { cli.list }.to_not output(/ignoretest.txt.erb/).to_stdout
49
+ end
50
+ end
51
+ context 'When an alternate extension is provided' do
52
+ before do
53
+ cli.options = { extension: 'tmplt', ignore: '.rndrignore', template: 'templates' }
54
+ end
55
+ it 'should list the correctly matched results' do
56
+ expect { cli.list }.to output(/exttest.txt.tmplt$/).to_stdout
57
+ expect { cli.list }.to_not output(/.erb/).to_stdout
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#render' do
63
+ let(:rendered_file) { File.absolute_path('../test/templates/rendertest.txt') }
64
+ let(:rendered_ext) { File.absolute_path('../test/templates/exttest.txt') }
65
+ let(:rendered_merged) do
66
+ File.join(File.dirname(__FILE__), 'resources/rendered_merged.txt')
67
+ end
68
+ let(:rendered_replaced) do
69
+ File.join(File.dirname(__FILE__), 'resources/rendered_replaced.txt')
70
+ end
71
+ after(:each) do
72
+ File.delete(rendered_file) if File.exist?(rendered_file)
73
+ File.delete(rendered_ext) if File.exist?(rendered_ext)
74
+ end
75
+
76
+ context 'When the defaults are spplied' do
77
+ before do
78
+ cli.options = {
79
+ extension: 'erb', ignore: '.rndrignore',
80
+ merge: true, template: 'templates', vars: 'vars'
81
+ }
82
+ end
83
+ it 'should render items correctly' do
84
+ expect { cli.render }.to output(/rendertest.txt \[OK\]$/).to_stdout
85
+ expect { cli.render }.to_not output(/ignoretest.txt/).to_stdout
86
+ expect(FileUtils.compare_file(rendered_merged, rendered_file)).to be_truthy
87
+ end
88
+ end
89
+
90
+ context ' When the replace merege strategy is specified' do
91
+ before do
92
+ cli.options = {
93
+ extension: 'erb', ignore: '.rndrignore',
94
+ merge: false, template: 'templates', vars: 'vars'
95
+ }
96
+ end
97
+ it 'should render items correctly' do
98
+ expect { cli.render }.to output(/rendertest.txt \[OK\]$/).to_stdout
99
+ expect { cli.render }.to_not output(/ignoretest.txt/).to_stdout
100
+ expect(FileUtils.compare_file(rendered_replaced, rendered_file)).to be_truthy
101
+ end
102
+ end
103
+
104
+ context 'When provided an alternate extension' do
105
+ before do
106
+ cli.options = {
107
+ extension: 'tmplt', ignore: '.rndrignore',
108
+ merge: true, template: 'templates', vars: 'vars'
109
+ }
110
+ end
111
+ it 'should render items correctly' do
112
+ expect { cli.render }.to output(/exttest.txt \[OK\]$/).to_stdout
113
+ expect { cli.render }.to_not output(/rendertest.txt/).to_stdout
114
+ expect(FileUtils.compare_file(rendered_merged, rendered_ext)).to be_truthy
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#vars' do
120
+ context 'When a directory is supplied.' do
121
+ context 'should serialize and recursively merge variables' do
122
+ before do
123
+ cli.options = { format: 'yaml', merge: true, vars: 'vars' }
124
+ @merged =
125
+ File.read(File.join(File.dirname(__FILE__), 'resources/vars_merged.txt'))
126
+ end
127
+ it 'should perform a deep merge' do
128
+ expect { cli.vars }.to output(@merged).to_stdout
129
+ end
130
+ end
131
+
132
+ context 'should serialize and merge variables with replace behaviour' do
133
+ before do
134
+ cli.options = { format: 'yaml', merge: false, vars: 'vars' }
135
+ @replaced =
136
+ File.read(File.join(File.dirname(__FILE__), 'resources/vars_replaced.txt'))
137
+ end
138
+ it 'should perform a standard merge' do
139
+ expect { cli.vars }.to output(@replaced).to_stdout
140
+ end
141
+ end
142
+ end
143
+
144
+ context 'When a json file is supplied' do
145
+ before do
146
+ cli.options = { format: 'yaml', merge: true, vars: 'vars/a.json' }
147
+ @vars =
148
+ File.read(File.join(File.dirname(__FILE__), 'resources/vars_a.txt'))
149
+ end
150
+ it 'should serialize the data' do
151
+ expect { cli.vars }.to output(@vars).to_stdout
152
+ end
153
+ end
154
+ context 'When a yaml file is supplied' do
155
+ before do
156
+ cli.options = { format: 'yaml', merge: true, vars: 'vars/b.yaml' }
157
+ @vars =
158
+ File.read(File.join(File.dirname(__FILE__), 'resources/vars_b.txt'))
159
+ end
160
+ it 'should serialize the data' do
161
+ expect { cli.vars }.to output(@vars).to_stdout
162
+ end
163
+ end
164
+ end
165
+
166
+ context '#version' do
167
+ it 'should print the version string' do
168
+ expect { cli.version }.to output(/Rndr Version: #{VERSION}$/).to_stdout
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,17 @@
1
+ ---
2
+ child:
3
+ childarray:
4
+ - arraykey1: arrayval1
5
+ - arraykey2: arrayval2
6
+ - arraykey3: arrayval3
7
+ - arraykey4: arrayval4
8
+ childhash:
9
+ childhashkey1: childhashval1
10
+ childhashkey2: childhashval2
11
+ childhashkey3: childhashval3
12
+ childhashkey4: childhashval4
13
+
14
+
15
+ {"boolkey":false,"numkey":2}
16
+
17
+ parent3: mergeval
@@ -0,0 +1,13 @@
1
+ ---
2
+ child:
3
+ childarray:
4
+ - arraykey3: arrayval3
5
+ - arraykey4: arrayval4
6
+ childhash:
7
+ childhashkey3: childhashval3
8
+ childhashkey4: childhashval4
9
+
10
+
11
+ {"boolkey":false,"numkey":2}
12
+
13
+ parent3: mergeval
@@ -0,0 +1,14 @@
1
+ ---
2
+ parent1:
3
+ child:
4
+ childarray:
5
+ - arraykey1: arrayval1
6
+ - arraykey2: arrayval2
7
+ childhash:
8
+ childhashkey1: childhashval1
9
+ childhashkey2: childhashval2
10
+ parent2:
11
+ boolkey: true
12
+ numkey: 1
13
+ parent3:
14
+ mergetest: mergeval
@@ -0,0 +1,12 @@
1
+ ---
2
+ parent1:
3
+ child:
4
+ childarray:
5
+ - arraykey3: arrayval3
6
+ - arraykey4: arrayval4
7
+ childhash:
8
+ childhashkey3: childhashval3
9
+ childhashkey4: childhashval4
10
+ parent2:
11
+ boolkey: false
12
+ numkey: 2
@@ -0,0 +1,18 @@
1
+ ---
2
+ parent1:
3
+ child:
4
+ childarray:
5
+ - arraykey1: arrayval1
6
+ - arraykey2: arrayval2
7
+ - arraykey3: arrayval3
8
+ - arraykey4: arrayval4
9
+ childhash:
10
+ childhashkey1: childhashval1
11
+ childhashkey2: childhashval2
12
+ childhashkey3: childhashval3
13
+ childhashkey4: childhashval4
14
+ parent2:
15
+ boolkey: false
16
+ numkey: 2
17
+ parent3:
18
+ mergetest: mergeval
@@ -0,0 +1,14 @@
1
+ ---
2
+ parent1:
3
+ child:
4
+ childarray:
5
+ - arraykey3: arrayval3
6
+ - arraykey4: arrayval4
7
+ childhash:
8
+ childhashkey3: childhashval3
9
+ childhashkey4: childhashval4
10
+ parent2:
11
+ boolkey: false
12
+ numkey: 2
13
+ parent3:
14
+ mergetest: mergeval
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'rspec'
5
+
6
+ if ENV['CI']
7
+ require 'coveralls'
8
+ Coveralls.wear!
9
+ end
10
+
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |expectations|
13
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
14
+ end
15
+
16
+ config.mock_with :rspec do |mocks|
17
+ mocks.verify_partial_doubles = true
18
+ end
19
+
20
+ config.shared_context_metadata_behavior = :apply_to_host_groups
21
+ config.disable_monkey_patching!
22
+ config.warnings = true
23
+ # config.formatter = 'documentation' unless defined? config.formatter
24
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'rndr'
5
+ require 'yaml'
6
+ require_relative 'spec_helper'
7
+
8
+ module Rndr
9
+ RSpec.describe Template do
10
+ before(:all) do
11
+ Dir.chdir(File.join(Dir.pwd, 'test'))
12
+ @success_tmplt = File.absolute_path('templates/rendertest.txt.erb')
13
+ @fail_tmplt = File.absolute_path('templates/failtest.txt.erb')
14
+ @rendered_success = File.absolute_path('templates/rendertest.txt')
15
+ @rendered_fail = File.absolute_path('templates/failtest.txt')
16
+ @compare_success = File.join(File.dirname(__FILE__), 'resources/rendered_merged.txt')
17
+ @vars =
18
+ YAML.load(File.read(File.join(File.dirname(__FILE__), 'resources/vars_merged.txt')))
19
+ end
20
+ after(:all) do
21
+ Dir.chdir('../')
22
+ File.delete(@rendered_success) if File.exist?(@rendered_success)
23
+ File.delete(@rendered_fail) if File.exist?(@rendered_fail)
24
+ end
25
+
26
+ describe '#render?' do
27
+ describe 'When the template is renderable' do
28
+ subject { Template.new(path: @success_tmplt, vars: @vars).render? }
29
+ it { is_expected.to be_truthy }
30
+ end
31
+
32
+ describe 'When the template is not renderable' do
33
+ subject { Template.new(path: @fail_tmplt, vars: @vars).render? }
34
+ it { is_expected.to be_falsey }
35
+ end
36
+ end
37
+
38
+ describe '#render' do
39
+ context 'When the template is renderable' do
40
+ before(:all) { File.delete(@rendered_success) if File.exist?(@rendered_success) }
41
+ subject { Template.new(path: @success_tmplt, vars: @vars).render(@rendered_success) }
42
+ it { is_expected.to be_truthy }
43
+ it 'should render file' do
44
+ expect(File.exist?(@rendered_success)).to be_truthy
45
+ end
46
+ it 'should render contents correctly' do
47
+ expect(FileUtils.compare_file(@rendered_success, @compare_success)).to be_truthy
48
+ end
49
+ end
50
+
51
+ context 'When the template is not renderable' do
52
+ before(:all) { File.delete(@rendered_fail) if File.exist?(@rendered_fail) }
53
+ subject { Template.new(path: @fail_tmplt, vars: @vars).render(@rendered_fail) }
54
+ it { is_expected.to be_falsey }
55
+ it 'should not render file' do
56
+ expect(File.exist?(@rendered_fail)).to be_falsey
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'json'
5
+ require 'rndr'
6
+ require 'rspec'
7
+ require 'yaml'
8
+ require_relative 'spec_helper'
9
+
10
+ module Rndr
11
+ RSpec.describe Rndr do
12
+ before(:all) { Dir.chdir(File.join(Dir.pwd, 'test')) }
13
+ after(:all) { Dir.chdir('../') }
14
+
15
+ describe '#matches' do
16
+ context 'When using the defaults' do
17
+ let(:matched) { [File.absolute_path('templates/rendertest.txt.erb')] }
18
+ let(:ignored) do
19
+ [File.absolute_path('templates/ignoretest.txt.erb'),
20
+ File.absolute_path('templates/failtest.txt.erb')]
21
+ end
22
+ subject(:results) { Rndr.matches(path: 'templates') }
23
+ it 'should be an Array of filtered paths with default extension' do
24
+ expect(results).to be_a(Array)
25
+ expect(results).to match_array(matched)
26
+ end
27
+ end
28
+
29
+ context 'When using an alternate extension' do
30
+ let(:matched) { [File.absolute_path('templates/exttest.txt.tmplt')] }
31
+ let(:ignored) do
32
+ [File.absolute_path('templates/ignoretest.txt.erb'),
33
+ File.absolute_path('templates/failtest.txt.erb')]
34
+ end
35
+ subject(:results) { Rndr.matches(path: 'templates', ext: 'tmplt') }
36
+ it 'should be an Array of paths with alternate extension' do
37
+ expect(results).to be_a(Array)
38
+ expect(results).to match_array(matched)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#read_vars' do
44
+ context 'When a directory is supplied' do
45
+ context 'should serialize and recursively merges variables' do
46
+ let(:results) do
47
+ YAML.load(File.read(File.join(File.dirname(__FILE__), 'resources/vars_merged.txt')))
48
+ end
49
+ subject(:vars) { Rndr.read_vars(path: 'vars', merge: true) }
50
+ it 'should perform a deep merge' do
51
+ expect(vars).to eql(results)
52
+ end
53
+ end
54
+ context 'should serialize and merge variables with replace behaviour' do
55
+ let(:results) do
56
+ YAML.load(File.read(File.join(File.dirname(__FILE__), 'resources/vars_replaced.txt')))
57
+ end
58
+ subject(:vars) { Rndr.read_vars(path: 'vars', merge: false) }
59
+ it 'should perform a standard merge' do
60
+ expect(vars).to eql(results)
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'When a json file is supplied' do
66
+ let(:results) { JSON.load(File.read('vars/a.json')) }
67
+ subject(:vars) { Rndr.read_vars(path: 'vars/a.json', merge: true) }
68
+ it 'should serialize the data' do
69
+ expect(vars).to eql(results)
70
+ end
71
+ end
72
+
73
+ context 'When a yaml file is supplied' do
74
+ let(:results) { YAML.load(File.read('vars/b.yaml')) }
75
+ subject(:vars) { Rndr.read_vars(path: 'vars/b.yaml', merge: true) }
76
+ it 'should serialize the data' do
77
+ expect(vars).to eql(results)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ #i r ignored
2
+ templates/ignoretest.txt.erb
3
+ templates/failtest.txt.erb
@@ -0,0 +1,5 @@
1
+ <%= parent1.to_yaml %>
2
+
3
+ <%= parent2.to_json %>
4
+
5
+ parent3: <%= parent3['mergetest'] %>
@@ -0,0 +1,5 @@
1
+ <%= nope.to_yaml %>
2
+
3
+ <%= nope2.to_json %>
4
+
5
+ parent3: <%= nope['itall'] %>
File without changes
@@ -0,0 +1,5 @@
1
+ <%= parent1.to_yaml %>
2
+
3
+ <%= parent2.to_json %>
4
+
5
+ parent3: <%= parent3['mergetest'] %>
@@ -0,0 +1,22 @@
1
+ {
2
+ "parent1": {
3
+ "child": {
4
+ "childarray": [{
5
+ "arraykey1": "arrayval1"
6
+ }, {
7
+ "arraykey2": "arrayval2"
8
+ }],
9
+ "childhash": {
10
+ "childhashkey1": "childhashval1",
11
+ "childhashkey2": "childhashval2"
12
+ }
13
+ }
14
+ },
15
+ "parent2": {
16
+ "boolkey": true,
17
+ "numkey": 1
18
+ },
19
+ "parent3": {
20
+ "mergetest": "mergeval"
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ parent1:
3
+ child:
4
+ childarray:
5
+ - arraykey3: arrayval3
6
+ - arraykey4: arrayval4
7
+ childhash:
8
+ childhashkey3: childhashval3
9
+ childhashkey4: childhashval4
10
+ parent2:
11
+ boolkey: false
12
+ numkey: 2
13
+
@@ -0,0 +1 @@
1
+ 0.0.2
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rndr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Bob Killen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deep_merge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.19.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.19.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.12.5
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.12.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 11.2.2
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 11.2.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.5.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.5.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: coveralls
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.15
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.15
97
+ description: rndr is a tool that simply renders Ruby erb templates for use with other
98
+ applications.
99
+ email:
100
+ - arcts-dev@umich.edu
101
+ executables:
102
+ - rndr
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".rubocop.yml"
109
+ - ".travis.yml"
110
+ - ".yardopts"
111
+ - Gemfile
112
+ - README.md
113
+ - Rakefile
114
+ - bin/rndr
115
+ - lib/rndr.rb
116
+ - lib/rndr/cli.rb
117
+ - lib/rndr/template.rb
118
+ - lib/rndr/util.rb
119
+ - lib/rndr/version.rb
120
+ - rndr.gemspec
121
+ - spec/cli_spec.rb
122
+ - spec/resources/rendered_merged.txt
123
+ - spec/resources/rendered_replaced.txt
124
+ - spec/resources/vars_a.txt
125
+ - spec/resources/vars_b.txt
126
+ - spec/resources/vars_merged.txt
127
+ - spec/resources/vars_replaced.txt
128
+ - spec/spec_helper.rb
129
+ - spec/template_spec.rb
130
+ - spec/util_spec.rb
131
+ - test/.rndrignore
132
+ - test/templates/exttest.txt.tmplt
133
+ - test/templates/failtest.txt.erb
134
+ - test/templates/ignoretest.txt.erb
135
+ - test/templates/rendertest.txt.erb
136
+ - test/vars/a.json
137
+ - test/vars/b.yaml
138
+ - version.txt
139
+ homepage: https://github.com/arc-ts/rndr
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '2.3'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.4.5
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Render Ruby templates quickly and easily.
163
+ test_files: []