kojo 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66b59e59e2a7d874db8c30218544c194798aa8b0e0680dbc1a906f3f75f67e59
4
- data.tar.gz: 56ff38e17c7cedd6df3373275fdacc65e9bc7fe8f2df5bdf7bdd650edea5eb37
3
+ metadata.gz: 60d550cda8b281a7ecbac40c236b6446fbe6a4d0b2ed7cb261ec47abeb654cbc
4
+ data.tar.gz: a991496e62aa272ad939b5c663cec543ed5e49aa9dedff5f3505c579d50de8d5
5
5
  SHA512:
6
- metadata.gz: 1202ad70051723bcbeceaf4ef641704f133685177dc42f9479bb7d4391d69e0b1d6b80245b684c68788627933be382843865654a6b7766e4ad6379d5963d4c89
7
- data.tar.gz: 312a0a592b342cef71bbe08f5c38c3db2007047fa052ae3f34faa65910d4cb41af60deb9b241804290a2ab3d2e42c269e8581ece1c543fae3ddf9c2349d94387
6
+ metadata.gz: aa51aa1c425c7af70bd8fd12bed004c0472d290e96cfdb172e3222f76d994a671eba764b45baae57f1287ebb4f7ba27fa94f0fbf725edc714095740653c9507a
7
+ data.tar.gz: 58802308a1134db6abd3265b0f7ccacd854fe4cdef6468f6c15217799f75eaf03aae0e9761a2b990201ae6c50cb680f2bb026f1f8869be86930bbff4dce36779
data/README.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  ![kojo](images/kojo.png)
4
4
 
5
- Kojo - Configuration Ninja
5
+ Kojo Configuration Ninja
6
6
  ==================================================
7
7
 
8
8
  [![Gem Version](https://badge.fury.io/rb/kojo.svg)](https://badge.fury.io/rb/kojo)
9
9
  [![Build Status](https://travis-ci.com/DannyBen/kojo.svg?branch=master)](https://travis-ci.com/DannyBen/kojo)
10
10
  [![Maintainability](https://api.codeclimate.com/v1/badges/f24566ad04b5054a2251/maintainability)](https://codeclimate.com/github/DannyBen/kojo/maintainability)
11
-
11
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/f24566ad04b5054a2251/test_coverage)](https://codeclimate.com/github/DannyBen/kojo/test_coverage)
12
12
 
13
13
  Kojo helps you generate configuration files from templates, using variables
14
14
  and definition files.
@@ -25,8 +25,8 @@ Table of Contents
25
25
  - [Usage](#usage)
26
26
  - [Variables](#variables)
27
27
  - [Import](#import)
28
- - [One to Many Generation](#one-to-many-generation)
29
28
  - [Compile an Entire Folder](#compile-an-entire-folder)
29
+ - [One to Many Generation](#one-to-many-generation)
30
30
  - [Conditions and Loops with ERB](#conditions-and-loops-with-erb)
31
31
 
32
32
  ---
@@ -41,7 +41,7 @@ Installation
41
41
  Usage
42
42
  --------------------------------------------------
43
43
 
44
- If you prefer to learn by example, see the [examples](examples) folder for
44
+ If you prefer to learn by example, see the [examples](examples#examples) folder for
45
45
  several use cases. Each example subfolder contains the command to run, the
46
46
  relevant files, and the expected output.
47
47
 
@@ -85,15 +85,28 @@ The space after `filename` is optional.
85
85
 
86
86
 
87
87
 
88
+ ### Compile an Entire Folder
89
+
90
+ ![kojo](images/features-dir.svg)
91
+
92
+ Process a folder containing templates and `@imports`, and generate a mirror
93
+ output folder, with all the variables and `@imports` evaluated.
94
+
95
+
96
+
88
97
  ### One to Many Generation
89
98
 
90
99
  ![kojo](images/features-config.svg)
91
100
 
92
- In order to generate several configuration files that are based on the same
93
- template, you should:
101
+ Using the `kojo config` command together with a simple definitions file, you
102
+ can:
103
+
104
+ 1. Generate multiple output files based on a single template file
105
+ 2. Generate multiple output directories, based on a single source directory.
106
+
107
+ To achieve this, you need to:
94
108
 
95
- 1. Create the configuration template, using `%{variables}` and `@imports`
96
- where appropriate.
109
+ 1. Create the configuration template or directory of templates.
97
110
  2. Create a configuration YAML file using this syntax:
98
111
 
99
112
  ```yaml
@@ -109,15 +122,22 @@ output:
109
122
  argument2: value
110
123
  ```
111
124
 
125
+ When using a folder as input, simply provide the folder name in the `input`
126
+ property, and instead of providing desired output filenames in the `output`
127
+ property, provide desired output directories:
112
128
 
129
+ ```yaml
130
+ input: base
113
131
 
114
- ### Compile an Entire Folder
115
-
116
- ![kojo](images/features-dir.svg)
117
-
118
- Process a folder containing templates and `@imports`, and generate a mirror
119
- output folder, with all the variables and `@imports` evaluated.
132
+ output:
133
+ app1:
134
+ argument1: value
135
+ argument2: value
120
136
 
137
+ app2:
138
+ argument1: value
139
+ argument2: value
140
+ ```
121
141
 
122
142
 
123
143
  ### Conditions and Loops with ERB
data/bin/kojo CHANGED
@@ -7,6 +7,6 @@ begin
7
7
  exit runner.run ARGV
8
8
  rescue => e
9
9
  puts e.backtrace.reverse if ENV['DEBUG']
10
- say! "!txtred!#{e.class}: #{e.message}"
10
+ say! "!undred!#{e.class}!txtrst!\n#{e.message}"
11
11
  exit 1
12
12
  end
data/lib/kojo.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'requires'
2
2
  require 'byebug' if ENV['BYEBUG']
3
- requires 'kojo'
3
+ requires 'kojo/exceptions', 'kojo'
4
4
 
5
5
  module Kojo
6
6
  module Commands
data/lib/kojo/cli.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'mister_bin'
2
2
 
3
3
  module Kojo
4
+ # The CLI class is used by the kojo binary and forwards incoming CLI
5
+ # commands to the relevant Kojo::Commands class
4
6
  class CLI
5
7
  def self.runner
6
8
  runner = MisterBin::Runner.new version: Kojo::VERSION
@@ -0,0 +1,38 @@
1
+ module Kojo
2
+ # The Collection class is a wrapper around the {Template} object. It
3
+ # provides a mechanism for processing an entire directory of templates.
4
+ class Collection
5
+ attr_reader :dir
6
+ attr_accessor :import_base
7
+
8
+ def initialize(dir)
9
+ @dir = dir
10
+ @import_base = dir
11
+ end
12
+
13
+ def render(args={}, &block)
14
+ files.each do |file|
15
+ handle file, args, &block
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def handle(file, args={})
22
+ template = Template.new file
23
+ template.import_base = import_base
24
+
25
+ path = file.sub(/#{dir}\//, '')
26
+
27
+ yield path, template.render(args)
28
+ end
29
+
30
+ def files
31
+ return @files if @files
32
+ raise Kojo::NotFoundError, "Directory not found: #{dir}" unless Dir.exist? dir
33
+ raise Kojo::NotFoundError, "Directory is empty: #{dir}" if Dir.empty? dir
34
+
35
+ @files = Dir["#{dir}/**/*"].reject { |f| File.directory? f }
36
+ end
37
+ end
38
+ end
@@ -2,15 +2,17 @@ require 'fileutils'
2
2
  require 'mister_bin'
3
3
 
4
4
  module Kojo::Commands
5
+ # Handle calls to the +kojo config+ command
5
6
  class ConfigCmd < MisterBin::Command
6
- attr_reader :gen, :outdir, :opts
7
+ attr_reader :gen, :outdir, :opts, :import_base, :config_file
7
8
 
8
9
  help "Generate based on instructions from a config file"
9
10
 
10
- usage "kojo config CONFIG [--save DIR --args FILE] [ARGS...]"
11
+ usage "kojo config CONFIG [--save DIR --imports DIR --args FILE] [ARGS...]"
11
12
  usage "kojo config (-h|--help)"
12
13
 
13
14
  option "-s --save DIR", "Save output to directory instead of printing"
15
+ option "-i --imports DIR", "Specify base directory for @import directives"
14
16
  option "-a --args FILE", "Load arguments from YAML file"
15
17
 
16
18
  param "ARGS", "Optional key=value pairs"
@@ -21,9 +23,10 @@ module Kojo::Commands
21
23
  example "kojo config config.yml -s output --args args.yml"
22
24
 
23
25
  def run(args)
24
- @gen = Kojo::Generator.new args['CONFIG']
26
+ @config_file = args['CONFIG']
25
27
  @outdir = args['--save']
26
28
  @opts = args['ARGS'].args_to_hash
29
+ @import_base = args['--imports']
27
30
  argfile = args['--args']
28
31
 
29
32
  if argfile
@@ -35,7 +38,10 @@ module Kojo::Commands
35
38
  end
36
39
 
37
40
  def run!
38
- gen.generate opts do |file, output|
41
+ config = Kojo::Config.new config_file
42
+ config.import_base = import_base if import_base
43
+
44
+ config.generate opts do |file, output|
39
45
  handle file, output
40
46
  end
41
47
  end
@@ -1,30 +1,31 @@
1
1
  require 'mister_bin'
2
2
 
3
3
  module Kojo::Commands
4
+ # Handle calls to the +kojo dir+ command
4
5
  class DirCmd < MisterBin::Command
5
6
  attr_reader :opts, :indir, :outdir, :import_base
6
7
 
7
8
  help "Compile a folder of templates to a similar output folder"
8
9
 
9
- usage "kojo dir INDIR [--save DIR --import DIR --args FILE] [ARGS...]"
10
+ usage "kojo dir INDIR [--save DIR --imports DIR --args FILE] [ARGS...]"
10
11
  usage "kojo dir (-h|--help)"
11
12
 
12
13
  option "-s --save DIR", "Save output to directory instead of printing"
13
- option "-i --import DIR", "Specify base directory for @import directives"
14
+ option "-i --imports DIR", "Specify base directory for @import directives"
14
15
  option "-a --args FILE", "Load arguments from YAML file"
15
16
 
16
17
  param "ARGS", "Optional key=value pairs"
17
18
 
18
19
  example "kojo dir indir"
19
20
  example "kojo dir in --save out env=production"
20
- example "kojo dir in --save out --import snippets env=production"
21
+ example "kojo dir in --save out --imports snippets env=production"
21
22
  example "kojo dir in -s out -i snippets -a args.yml"
22
23
 
23
24
  def run(args)
24
25
  @opts = args['ARGS'].args_to_hash
25
26
  @indir = args['INDIR']
26
27
  @outdir = args['--save']
27
- @import_base = args['--import']
28
+ @import_base = args['--imports']
28
29
  argfile = args['--args']
29
30
 
30
31
  if argfile
@@ -37,32 +38,32 @@ module Kojo::Commands
37
38
 
38
39
  private
39
40
 
40
- def files
41
- @files ||= Dir["#{indir}/**/*"].reject { |file| File.directory? file }
42
- end
43
-
44
41
  def run!
45
- files.each do |file|
46
- handle file
42
+ collection = Kojo::Collection.new @indir
43
+ collection.import_base = import_base if import_base
44
+
45
+ if outdir
46
+ write collection
47
+ else
48
+ show collection
47
49
  end
48
50
  end
49
51
 
50
- def handle(file)
51
- template = Kojo::Template.new(file, opts)
52
- template.import_base = import_base if import_base
53
- output = template.render
52
+ def show(collection)
53
+ collection.render @opts do |file, output|
54
+ say "\n!txtgrn!# #{file}"
55
+ say output
56
+ end
57
+ end
54
58
 
55
- if outdir
59
+ def write(collection)
60
+ collection.render @opts do |file, output|
56
61
  save file, output
57
- else
58
- outpath = file.sub(/#{indir}/, '')
59
- say "\n!txtgrn!# #{outpath}"
60
- say output
61
62
  end
62
63
  end
63
64
 
64
65
  def save(file, output)
65
- outpath = file.sub(/#{indir}/, outdir)
66
+ outpath = "#{outdir}/#{file}"
66
67
  dir = File.dirname outpath
67
68
  FileUtils.mkdir_p dir unless Dir.exist? dir
68
69
  File.write outpath, output
@@ -1,15 +1,17 @@
1
1
  require 'mister_bin'
2
2
 
3
3
  module Kojo::Commands
4
+ # Handle calls to the +kojo file+ command
4
5
  class FileCmd < MisterBin::Command
5
- attr_reader :opts, :outfile, :infile
6
+ attr_reader :opts, :outfile, :infile, :import_base
6
7
 
7
8
  help "Compile a file from a template"
8
9
 
9
- usage "kojo file INFILE [--save FILE --args FILE] [ARGS...]"
10
+ usage "kojo file INFILE [--save FILE --imports DIR --args FILE] [ARGS...]"
10
11
  usage "kojo file (-h|--help)"
11
12
 
12
13
  option "-s --save FILE", "Save to file instead of printing"
14
+ option "-i --imports DIR", "Specify base directory for @import directives"
13
15
  option "-a --args FILE", "Load arguments from YAML file"
14
16
 
15
17
  param "ARGS", "Optional key=value pairs"
@@ -23,6 +25,7 @@ module Kojo::Commands
23
25
  @opts = args['ARGS'].args_to_hash
24
26
  @outfile = args['--save']
25
27
  @infile = args['INFILE']
28
+ @import_base = args['--imports']
26
29
  argfile = args['--args']
27
30
 
28
31
  if argfile
@@ -34,7 +37,9 @@ module Kojo::Commands
34
37
  end
35
38
 
36
39
  def run!
37
- output = Kojo::Template.new(infile, opts).render
40
+ template = Kojo::Template.new infile
41
+ template.import_base = import_base if import_base
42
+ output = template.render(opts)
38
43
 
39
44
  if outfile
40
45
  File.write outfile, output
@@ -0,0 +1,66 @@
1
+ require 'yaml'
2
+
3
+ module Kojo
4
+ # The Config class handles multiple template generation from a
5
+ # definitions YAML file.
6
+ class Config
7
+ attr_reader :config_file, :outdir
8
+ attr_accessor :import_base
9
+
10
+ def initialize(config_file)
11
+ @config_file = config_file
12
+ @import_base = nil
13
+ end
14
+
15
+ def generate(opts={}, &block)
16
+ if directory_mode?
17
+ generate_from_dir opts, &block
18
+ else
19
+ generate_from_file opts, &block
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def generate_from_file(opts)
26
+ config['output'].each do |target, config_opts|
27
+ local_opts = opts.merge config_opts.symbolize_keys
28
+
29
+ template = Template.new source
30
+ template.import_base = import_base if import_base
31
+
32
+ yield target, template.render(local_opts)
33
+ end
34
+ end
35
+
36
+ def generate_from_dir(opts)
37
+ config['output'].each do |dir, config_opts|
38
+ local_opts = opts.merge config_opts.symbolize_keys
39
+
40
+ collection = Collection.new source
41
+ collection.import_base = import_base if import_base
42
+
43
+ collection.render(local_opts) do |file, output|
44
+ yield "#{dir}/#{file}", output
45
+ end
46
+ end
47
+ end
48
+
49
+ def base_dir
50
+ @base_dir ||= File.dirname config_file
51
+ end
52
+
53
+ def directory_mode?
54
+ File.directory? source
55
+ end
56
+
57
+ def source
58
+ "#{base_dir}/#{config['input']}"
59
+ end
60
+
61
+ def config
62
+ @config ||= YAML.load_file config_file
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,5 @@
1
+ module Kojo
2
+ class Error < StandardError; end
3
+ class TemplateError < Error; end
4
+ class NotFoundError < Error; end
5
+ end
@@ -1,6 +1,7 @@
1
1
  require 'kojo/extensions/hash'
2
2
 
3
3
  class Array
4
+ # Convert an array of +["key=value", "key=value"]+ pairs to a hash
4
5
  def args_to_hash
5
6
  collect { |a| k, v = a.split '=' }
6
7
  .to_h
@@ -1,4 +1,5 @@
1
1
  class String
2
+ # Convert a string to the most appropriate type
2
3
  def to_typed
3
4
  if self =~ /\A[+-]?\d+\Z/
4
5
  self.to_i
@@ -6,8 +7,8 @@ class String
6
7
  elsif self =~ /\A[+-]?\d+\.\d+\Z/
7
8
  self.to_f
8
9
 
9
- elsif %w[yes no true false].include? self.downcase
10
- %w[yes true].include? self.downcase
10
+ elsif %w[yes no true false].include? downcase
11
+ %w[yes true].include? downcase
11
12
 
12
13
  else
13
14
  self
data/lib/kojo/template.rb CHANGED
@@ -2,30 +2,53 @@ require 'erb'
2
2
  require 'ostruct'
3
3
 
4
4
  module Kojo
5
+ # The Template class handles a single template file, and processes it for:
6
+ # - Variables (using +%{var}+ syntax)
7
+ # - ERB
8
+ # - +@import+ statements
5
9
  class Template
6
- attr_reader :file, :extension, :args, :dir
10
+ attr_reader :file, :extension, :dir, :args
7
11
  attr_accessor :import_base
8
12
 
9
- def initialize(file, args={})
13
+ def initialize(file)
10
14
  @file = file
11
- @args = args
12
- @extension = file[/(\..*)$/]
15
+ @extension = File.extname file
13
16
  @dir = File.dirname file
14
17
  @import_base = dir
15
18
  end
16
19
 
17
- def render
18
- evaluate file, args
20
+ def render(args={})
21
+ @args = args
22
+ evaluate file
19
23
  end
20
24
 
21
25
  private
22
26
 
23
- def evaluate(file, args={})
24
- content = File.read(file)
27
+ def evaluate(file)
28
+ content = read_file file
29
+ content = eval_erb content
30
+ content = eval_vars content
31
+ content = eval_imports content
32
+ content
33
+ end
34
+
35
+ def read_file(file)
36
+ raise Kojo::NotFoundError, "File not found: #{file}" unless File.exist? file
37
+ File.read file
38
+ end
39
+
40
+ def eval_vars(content)
41
+ content % args
42
+ rescue ArgumentError, KeyError => e
43
+ raise Kojo::TemplateError, "#{e.message}\nin: #{file}"
44
+ end
25
45
 
26
- content = erb content, args
27
- content = content % args
28
- content = eval_imports content
46
+ def eval_erb(content)
47
+ erb content, args
48
+ rescue RuntimeError => e
49
+ raise Kojo::TemplateError, "Invalid Ruby code #{e.message}\nin: #{file}"
50
+ rescue SyntaxError => e
51
+ raise Kojo::TemplateError, "#{e.message}\nin: #{file}"
29
52
  end
30
53
 
31
54
  def eval_imports(content)
@@ -52,7 +75,7 @@ module Kojo
52
75
  def import(file, import_args={})
53
76
  filename = File.expand_path "#{file}#{extension}", import_base
54
77
  all_args = args.merge import_args
55
- self.class.new(filename, all_args).render
78
+ self.class.new(filename).render(all_args)
56
79
  end
57
80
 
58
81
  def erb(template, vars)
data/lib/kojo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kojo
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kojo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Ben Shitrit
@@ -134,13 +134,15 @@ files:
134
134
  - bin/kojo
135
135
  - lib/kojo.rb
136
136
  - lib/kojo/cli.rb
137
+ - lib/kojo/collection.rb
137
138
  - lib/kojo/commands/config.rb
138
139
  - lib/kojo/commands/dir.rb
139
140
  - lib/kojo/commands/file.rb
141
+ - lib/kojo/config.rb
142
+ - lib/kojo/exceptions.rb
140
143
  - lib/kojo/extensions/array.rb
141
144
  - lib/kojo/extensions/hash.rb
142
145
  - lib/kojo/extensions/string.rb
143
- - lib/kojo/generator.rb
144
146
  - lib/kojo/template.rb
145
147
  - lib/kojo/version.rb
146
148
  homepage: https://github.com/dannyben/kojo
@@ -1,33 +0,0 @@
1
- require 'yaml'
2
-
3
- module Kojo
4
- class Generator
5
- attr_reader :config_file, :outdir
6
-
7
- def initialize(config_file)
8
- @config_file = config_file
9
- end
10
-
11
- def generate(opts={})
12
- base_dir = File.dirname config_file
13
- infile = "#{base_dir}/#{config['input']}"
14
-
15
- config['output'].each do |outfile, config_opts|
16
- local_opts = opts.merge config_opts.symbolize_keys
17
- output = render infile, local_opts
18
- yield outfile, output
19
- end
20
- end
21
-
22
- private
23
-
24
- def config
25
- @config ||= YAML.load_file config_file
26
- end
27
-
28
- def render(infile, opts={})
29
- Template.new(infile, opts).render
30
- end
31
- end
32
-
33
- end