planter-cli 3.0.4 → 3.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.irbrc +2 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +20 -0
- data/README.md +131 -45
- data/bin/plant +5 -4
- data/lib/planter/array.rb +15 -1
- data/lib/planter/color.rb +7 -1
- data/lib/planter/config.rb +172 -0
- data/lib/planter/filelist.rb +12 -2
- data/lib/planter/numeric.rb +31 -0
- data/lib/planter/plant.rb +24 -17
- data/lib/planter/prompt.rb +23 -11
- data/lib/planter/script.rb +3 -3
- data/lib/planter/string.rb +150 -74
- data/lib/planter/tag.rb +38 -0
- data/lib/planter/version.rb +1 -1
- data/lib/planter.rb +39 -108
- data/lib/tty-spinner/lib/tty/spinner/multi.rb +3 -3
- data/planter-cli.gemspec +2 -0
- data/spec/cli_spec.rb +12 -2
- data/spec/planter/filelist_spec.rb +2 -2
- data/spec/planter/prompt_spec.rb +71 -0
- data/spec/planter/script_spec.rb +5 -4
- data/spec/planter/string_spec.rb +26 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/templates/test/_planter.yml +2 -0
- data/spec/templates/test/_scripts/plant.sh +3 -0
- data/src/_README.md +131 -45
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff00e3dd5814379c26e559c2183c04907d1082a64d4545af978192e59cb3139e
|
4
|
+
data.tar.gz: 97de83aec4990751e0e1ec91213f5ff18a87e98f073726cec84834599ebaf66a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ececba941990543bca716162fc14ab187e72a8b330100763448e4170b4229848c7d9dae967a9ac0e75d88d6197e141f6db8bd89e528dc6beab561e83dafc64e
|
7
|
+
data.tar.gz: 0030f70d32826d28478f880ed7b17e51d8bb1312830815e27cfd050475bd720a759393e1ed3c17de618f41b7b7e14cfe47e2ad8a863eb52bdab1732636a37b49
|
data/.irbrc
CHANGED
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
### 3.0.5
|
2
|
+
|
3
|
+
2024-09-04 07:49
|
4
|
+
|
5
|
+
#### NEW
|
6
|
+
|
7
|
+
- Date_format setting for date types
|
8
|
+
- Allow `today '%Y'` inline date formatting
|
9
|
+
- If/then logic for file operators
|
10
|
+
- First_letter, first_word modifiers for variables
|
11
|
+
- Allow default/value variable configutations to include %%placeholders%% and modifiers
|
12
|
+
|
13
|
+
#### IMPROVED
|
14
|
+
|
15
|
+
- Code refactoring
|
16
|
+
|
17
|
+
#### FIXED
|
18
|
+
|
19
|
+
- Overwrite operation not overwriting
|
20
|
+
|
1
21
|
### 3.0.4
|
2
22
|
|
3
23
|
2024-09-02 08:43
|
data/README.md
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
|
3
3
|
Plant a directory structure and files using templates.
|
4
4
|
|
5
|
+
|
6
|
+
- [Planter](#planter)
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Configuration](#configuration)
|
9
|
+
- [Scripts](#scripts)
|
10
|
+
- [Templates](#templates)
|
11
|
+
- [Multiple choice type](#multiple-choice-type)
|
12
|
+
- [File-specific handling](#file-specific-handling)
|
13
|
+
- [If/then logic for file handling](#ifthen-logic-for-file-handling)
|
14
|
+
- [Regex replacements](#regex-replacements)
|
15
|
+
- [Finder Tags](#finder-tags)
|
16
|
+
- [Placeholders](#placeholders)
|
17
|
+
- [Default values in template strings](#default-values-in-template-strings)
|
18
|
+
- [Modifiers](#modifiers)
|
19
|
+
- [If/then logic](#ifthen-logic)
|
20
|
+
- [Usage](#usage)
|
21
|
+
- [Documentation](#documentation)
|
22
|
+
- [Development and Testing](#development-and-testing)
|
23
|
+
- [Source Code](#source-code)
|
24
|
+
- [Requirements](#requirements)
|
25
|
+
- [Rake](#rake)
|
26
|
+
- [Guard](#guard)
|
27
|
+
- [Contributing](#contributing)
|
28
|
+
- [License](#license)
|
29
|
+
- [Warranty](#warranty)
|
30
|
+
|
31
|
+
|
5
32
|
## Installation
|
6
33
|
|
7
34
|
gem install planter-cli
|
@@ -33,7 +60,7 @@ Scripts can be executable files in any language, and receive the template direct
|
|
33
60
|
|
34
61
|
Templates are directories found in `~/.config/planter/templates/[TEMPLATE_NAME]`. All files and directories inside of these template directories are copied when that template is called. Filenames, directory names, and file contents can all use template placeholders.
|
35
62
|
|
36
|
-
Template placeholders are defined with `%%KEY%%`, where key is the key defined in the `variables` section of the configuration. `%%KEY%%` placeholders can be used in directory/file names, and in the file contents. These work in any plain text or RTF format document, including XML, so they can be used in things like Scrivener templates and MindNode files as well.
|
63
|
+
Template placeholders are defined with `%%KEY%%`, where key is the key defined in the `variables` section of the configuration. `%%KEY%%` placeholders can be used in directory/file names, and in the file contents. These work in any plain text or RTF format document, including XML, so they can be used in things like Scrivener templates and MindNode files as well. See the subsections for info on default values and modifiers for template placeholders.
|
37
64
|
|
38
65
|
Each template contains a `_planter.yml` file that defines variables and other configuration options. The file format for all configuration files is [YAML](https://yaml.org/spec/1.2.2/).
|
39
66
|
|
@@ -44,12 +71,15 @@ variables:
|
|
44
71
|
- key: var_key
|
45
72
|
prompt: Prompt text
|
46
73
|
type: string # [string,paragraph,float,integer,number,date,choice] defaults to string
|
47
|
-
|
74
|
+
value: # (force value, string can include %%variables%% and regexes will be replaced. For date type can be today, time, now, etc.)
|
48
75
|
default: Untitled
|
49
76
|
min: 1
|
50
77
|
max: 5
|
78
|
+
date_format: "%Y-%m-%d" # can be any strftime format, will be applied to any date type
|
51
79
|
```
|
52
80
|
|
81
|
+
For the date type, value can be `today`, `now`, `tomorrow`, `last thursday`, etc. and natural language will be converted to a time. The formatting will be determined automatically based on the type of the value, e.g. "now" gets a time string, but "today" just gets a YYYY-mm-dd string. This can be modified using the `date_format` key, which accepts any [strftime](https://www.fabriziomusacchio.com/blog/2021-08-15-strftime_Cheat_Sheet/) value. You can also include a date format string in single parenthesis after a natural language date value, e.g. `value: "now 'This year: %Y'"`.
|
82
|
+
|
53
83
|
A configuration can include additional keys:
|
54
84
|
|
55
85
|
```yaml
|
@@ -61,9 +91,6 @@ replacements: # Dictionary of pattern/replacments for regex substitution, see [R
|
|
61
91
|
repo: # If a repository URL is provided, it will be pulled and duplicated instead of copying an existing file structure
|
62
92
|
```
|
63
93
|
|
64
|
-
#### Default values in template strings
|
65
|
-
|
66
|
-
In a template you can add a default value for a placholder by adding `%default value` to it. For example, `%%project%Default Project%%` will set the placeholder to `Default Project` if the variable value matches the default value in the configuration (or doesn't exist). This allows you to accept the default on the command line but have a different value inserted in the template. To use another variable in its place, use `$KEY` in the placeholder, e.g. `%%project%$title%%` will replace the `project` key with the value of `title` if the default is selected. Modifiers can be used on either side of the `%`, e.g. `%%project%$title:snake%%`.
|
67
94
|
|
68
95
|
#### Multiple choice type
|
69
96
|
|
@@ -103,48 +130,35 @@ variables:
|
|
103
130
|
- 1. zsh
|
104
131
|
```
|
105
132
|
|
106
|
-
If the choice starts with a number (as above), then a numeric list will be generated and typing the associated index number will accept that choice. Numeric lists are automatically numbered, so the preceding digit doesn't matter, as long as it's a digit. In this case a default can be defined with an integer (in the `defaults:` key) for its placement in the list (starting with 1), and parenthesis aren't required.
|
107
|
-
|
108
|
-
#### If/then logic
|
109
|
-
|
110
|
-
A template can use if/then logic, which is useful with multiple choice types. It can be applied to any type, though.
|
111
|
-
|
112
|
-
The format for if/then logic is:
|
113
|
-
|
114
|
-
```
|
115
|
-
%%if KEY OPERATOR VALUE%%
|
116
|
-
content
|
117
|
-
%%else if KEY OPERATOR VALUE2%%
|
118
|
-
content 2
|
119
|
-
%%else%%
|
120
|
-
content 3
|
121
|
-
%%endif%%
|
122
|
-
```
|
123
|
-
|
124
|
-
There should be no spaces around the comparison, e.g. `%% if language == javascript %%` won't work. The block must start with an `if` statement and end with `%%endif%%` or `%%end%%`. The `%%else%%` statement is optional -- if it doesn't exist then the entire block will be removed if no conditions are met.
|
133
|
+
If the choice starts with a number (as above), then a numeric list will be generated and typing the associated index number will accept that choice. Numeric lists are automatically numbered, so the preceding digit doesn't matter, as long as it's a digit, and only the first item needs a digit to trigger numbering. In this case a default can be defined with an integer (in the `defaults:` key) for its placement in the list (starting with 1), and parenthesis aren't required.
|
125
134
|
|
126
|
-
|
135
|
+
`value:` and `default:` can include previous variables, with modifiers:
|
127
136
|
|
128
|
-
|
129
|
-
-
|
130
|
-
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
-
|
136
|
-
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
137
|
+
```yaml
|
138
|
+
- variables:
|
139
|
+
- key: language
|
140
|
+
prompt: Programming language
|
141
|
+
type: choice
|
142
|
+
default: 2
|
143
|
+
choices:
|
144
|
+
- 1. ruby
|
145
|
+
- javascript
|
146
|
+
- python
|
147
|
+
- bash
|
148
|
+
- zsh
|
149
|
+
- key: shebang
|
150
|
+
prompt: Shebang line
|
151
|
+
type: choice
|
152
|
+
default: "%%language:first_letter:lowercase%%"
|
153
|
+
choices:
|
154
|
+
(r)uby: "#! /usr/bin/env ruby"
|
155
|
+
(j)avascript: "#! /usr/bin/env node"
|
156
|
+
(p)ython: "#! /usr/bin/env python"
|
157
|
+
(b)ash: "#! /bin/bash"
|
158
|
+
(z)sh: "#! /bin/zsh"
|
144
159
|
```
|
145
160
|
|
146
|
-
|
147
|
-
|
161
|
+
The above would define the `language` variable first, accepting `javascript` as default, then when displaying the menu for `shebang`, the language variable would have its first letter lowercased and set as the default option for the menu.
|
148
162
|
|
149
163
|
### File-specific handling
|
150
164
|
|
@@ -176,7 +190,17 @@ Merged content
|
|
176
190
|
|
177
191
|
By default files that already exist in the destination directory are not overwritten, and merging allows you to add missing parts to a Rakefile or Makefile, for example.
|
178
192
|
|
179
|
-
If `ask` is specified, a
|
193
|
+
If `ask` is specified, a menu will be provided on the command line asking how to handle a file. If the file doesn't already exist, you will be asked only whether to copy the file or not. If it does exist, `overwrite` and `merge` options will be added.
|
194
|
+
|
195
|
+
#### If/then logic for file handling
|
196
|
+
|
197
|
+
The operation for a file match can be an if/then statement. There are two formats for this.
|
198
|
+
|
199
|
+
First, you can simply write `OPERATION if VARIABLE COMP VALUE`, e.g. `copy if language == ruby`. This can have an `else` statement: `overwrite if language == ruby else copy`.
|
200
|
+
|
201
|
+
You can also format it as `if VARIABLE COMP VALUE: OPERATION; else: OPERATION`. This format allows `else if` statements, e.g. `if language == perl: copy;else if language == ruby: overwrite; else: ignore`.
|
202
|
+
|
203
|
+
Planter's if/then parsing does not handle parenthetical or boolean operations.
|
180
204
|
|
181
205
|
### Regex replacements
|
182
206
|
|
@@ -194,6 +218,69 @@ Replacements are performed on both file/directory names and file contents. This
|
|
194
218
|
|
195
219
|
If `preserve_tags` is set to `true` in the config (either base or template), then existing Finder tags on the file or folder will be copied to the new file when a template is planted.
|
196
220
|
|
221
|
+
### Placeholders
|
222
|
+
|
223
|
+
Placeholders are `%%VARIABLE%%` strings used in filenames and within the content of the files. At their most basic, this would just look like `%%project_name%%`. But you can supply default values, string modifiers, and even if/then logic.
|
224
|
+
|
225
|
+
#### Default values in template strings
|
226
|
+
|
227
|
+
In a template you can add a default value for a placholder by adding `%default value` to it. For example, `%%project%Default Project%%` will set the placeholder to `Default Project` if the variable value matches the default value in the configuration (or doesn't exist). This allows you to accept the default on the command line but have a different value inserted in the template. To use another variable in its place, use `$KEY` in the placeholder, e.g. `%%project%$title%%` will replace the `project` key with the value of `title` if the default is selected. Modifiers can be used on either side of the `%`, e.g. `%%project%$title:snake%%`.
|
228
|
+
|
229
|
+
#### Modifiers
|
230
|
+
|
231
|
+
A `%%variable%%` in a template can include modifiers that affect the output of the variable. These are added as `%%variable:MODIFIER%%` and multiple modifiers can be strung together, e.g. `%%language:lowercase:first_letter%%`. The available modifiers are (listed with available abbreviations):
|
232
|
+
|
233
|
+
- `snake` (`s`): snake_case the value
|
234
|
+
- `camel` (`cam`): camelCase the value
|
235
|
+
- `upper` (`u`): UPPERCASE the value
|
236
|
+
- `lower` (`l`, `d`): lowercase the value
|
237
|
+
- `title` (`t`): Title Case The Value
|
238
|
+
- `slug` or `file` (`sl`): slug-format-the-value
|
239
|
+
- `first_letter` (`fl`): Extract the first letter from the value
|
240
|
+
- `first_word` (`fw`): Extract the first word from the value
|
241
|
+
|
242
|
+
#### If/then logic
|
243
|
+
|
244
|
+
A template can use if/then logic, which is useful with multiple choice types. It can be applied to any type, though.
|
245
|
+
|
246
|
+
The format for if/then logic is:
|
247
|
+
|
248
|
+
```
|
249
|
+
%%if KEY OPERATOR VALUE%%
|
250
|
+
content
|
251
|
+
%%else if KEY OPERATOR VALUE2%%
|
252
|
+
content 2
|
253
|
+
%%else%%
|
254
|
+
content 3
|
255
|
+
%%endif%%
|
256
|
+
```
|
257
|
+
|
258
|
+
There should be no spaces around the comparison, e.g. `%% if language == javascript %%` won't work. The block must start with an `if` statement and end with `%%endif%%` or `%%end%%`. The `%%else%%` statement is optional -- if it doesn't exist then the entire block will be removed if no conditions are met.
|
259
|
+
|
260
|
+
The key should be an existing key defined in `variables`. The operator can be any of:
|
261
|
+
|
262
|
+
- `==` or `=` (equals)
|
263
|
+
- `=~` (matches regex)
|
264
|
+
- `*=` (contains)
|
265
|
+
- `^=` (starts with)
|
266
|
+
- `$=` (ends with)
|
267
|
+
- `>` (greater than)
|
268
|
+
- `>=` (greater than or equal)
|
269
|
+
- `<` (less than)
|
270
|
+
- `<=` (less than or equal)
|
271
|
+
|
272
|
+
Any of these operators can be inverted (negated) by preceding with an exclamation point, e.g. `!=` means `not equal` and `!*=` means `does not contain`.
|
273
|
+
|
274
|
+
The value after the operator doesn't need to be quoted, anything after the operator will be compared to the value of the key.
|
275
|
+
|
276
|
+
Logic can be used on multiple lines like the example above, or on a single line (useful for filenames):
|
277
|
+
|
278
|
+
|
279
|
+
%%project%%.%%if language == javascript%%js%%else if language == ruby%%rb%%else%%sh%%endif%%
|
280
|
+
|
281
|
+
|
282
|
+
Content within if/else blocks can contain variables. Planter's if/then parsing does not handle parenthetical or boolean operations.
|
283
|
+
|
197
284
|
## Usage
|
198
285
|
|
199
286
|
The executable for Planter is `plant`. You can run `plant TEMPLATE` in any directory and TEMPLATE will be planted in the current directory. You can also use `--in PATH` to plant in another directory.
|
@@ -222,7 +309,6 @@ Variables can be passed on the command line with `--var KEY:VALUE`. This flag ca
|
|
222
309
|
## Documentation
|
223
310
|
|
224
311
|
- [YARD documentation][RubyDoc] is hosted by RubyDoc.info.
|
225
|
-
- [Interactive documentation][Omniref] is hosted by Omniref.
|
226
312
|
|
227
313
|
[RubyDoc]: http://www.rubydoc.info/gems/planter-cli
|
228
314
|
|
data/bin/plant
CHANGED
@@ -79,8 +79,9 @@ opts.parse!
|
|
79
79
|
##
|
80
80
|
def list_vars(template)
|
81
81
|
puts "#{template} variables:"
|
82
|
-
Planter.
|
83
|
-
|
82
|
+
Planter.template = template
|
83
|
+
@config = Planter::Config.new
|
84
|
+
@config.variables.sort_by { |v| v[:key].to_var }.each do |var|
|
84
85
|
title = var[:prompt] || var[:key]
|
85
86
|
var_type = var[:type].normalize_type || :string
|
86
87
|
default = var[:value] || var[:default]
|
@@ -101,12 +102,12 @@ elsif options[:help]
|
|
101
102
|
end
|
102
103
|
Process.exit 0
|
103
104
|
elsif ARGV.count.zero?
|
104
|
-
|
105
|
+
die('Template argument required', :argument)
|
105
106
|
end
|
106
107
|
|
107
108
|
ARGV.each do |template|
|
108
109
|
# Planter.spinner.update(title: 'Initializing configuration')
|
109
|
-
Planter.
|
110
|
+
Planter.template = template
|
110
111
|
app = Planter::Plant.new
|
111
112
|
app.plant
|
112
113
|
end
|
data/lib/planter/array.rb
CHANGED
@@ -9,10 +9,17 @@ module Planter
|
|
9
9
|
##
|
10
10
|
## @example ["(c)hoice", "(o)ther"].abbr_choices #=> "[c/o]"
|
11
11
|
##
|
12
|
-
## @param default [String] The color templated output string
|
12
|
+
## @param default [String] The (unprocessed) color templated output string
|
13
13
|
##
|
14
14
|
def abbr_choices(default: nil)
|
15
|
+
return default.nil? ? '' : "{xdw}[{xbc}#{default}{dw}]{x}" if all? { |c| c.to_i.positive? }
|
16
|
+
|
15
17
|
chars = join(' ').scan(/\((?:(.)\.?)\)/).map { |c| c[0] }
|
18
|
+
|
19
|
+
return default.nil? ? '' : "{xdw}[{xbc}#{default}{dw}]{x}" if chars.all? { |c| c.to_i.positive? }
|
20
|
+
|
21
|
+
die('Array contains duplicates', :input) if chars.duplicates?
|
22
|
+
|
16
23
|
out = String.new
|
17
24
|
out << '{xdw}['
|
18
25
|
out << chars.map do |c|
|
@@ -32,6 +39,8 @@ module Planter
|
|
32
39
|
## @return [Array] Array of choices
|
33
40
|
##
|
34
41
|
def to_options(numeric)
|
42
|
+
die('Array contains duplicates', :input) if duplicates?
|
43
|
+
|
35
44
|
map.with_index do |c, i|
|
36
45
|
# v = c.to_s.match(/\(?([a-z]|\d+\.?)\)?/)[1].strip
|
37
46
|
if numeric
|
@@ -42,6 +51,11 @@ module Planter
|
|
42
51
|
end
|
43
52
|
end
|
44
53
|
|
54
|
+
## test if array has duplicates
|
55
|
+
def duplicates?
|
56
|
+
uniq.size != size
|
57
|
+
end
|
58
|
+
|
45
59
|
## Find the index of a choice in an array of choices
|
46
60
|
##
|
47
61
|
## @param choice [String] The choice to find
|
data/lib/planter/color.rb
CHANGED
@@ -108,6 +108,12 @@ module Planter
|
|
108
108
|
Color.template(self)
|
109
109
|
end
|
110
110
|
|
111
|
+
## Remove template codes from a string
|
112
|
+
## @return [String] uncolored string
|
113
|
+
def strip_template
|
114
|
+
gsub(/(?<!\\)\{(\w+)\}/i, '')
|
115
|
+
end
|
116
|
+
|
111
117
|
##
|
112
118
|
## Extract the longest valid %color name from a string.
|
113
119
|
##
|
@@ -243,7 +249,7 @@ module Planter
|
|
243
249
|
##
|
244
250
|
def template(input)
|
245
251
|
input = input.join(' ') if input.is_a? Array
|
246
|
-
return input.
|
252
|
+
return input.strip_template unless Color.coloring?
|
247
253
|
|
248
254
|
input = input.gsub(/(?<!\\)\{((?:[fb]g?)?#[a-f0-9]{3,6})\}/i) do
|
249
255
|
hex = Regexp.last_match(1)
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
class Config < Hash
|
5
|
+
attr_reader :template
|
6
|
+
|
7
|
+
##
|
8
|
+
## Initialize a new Config object for a template
|
9
|
+
##
|
10
|
+
## @param template [String] template name
|
11
|
+
##
|
12
|
+
def initialize
|
13
|
+
# super()
|
14
|
+
|
15
|
+
@config = initial_config
|
16
|
+
@template = Planter.template
|
17
|
+
|
18
|
+
load_template
|
19
|
+
|
20
|
+
die('No configuration found', :config) unless @config
|
21
|
+
|
22
|
+
generate_accessors
|
23
|
+
end
|
24
|
+
|
25
|
+
def initial_config
|
26
|
+
{
|
27
|
+
defaults: false,
|
28
|
+
git_init: false,
|
29
|
+
files: { '_planter.yml' => 'ignore' },
|
30
|
+
color: true,
|
31
|
+
preserve_tags: nil,
|
32
|
+
variables: nil,
|
33
|
+
replacements: nil,
|
34
|
+
repo: false,
|
35
|
+
patterns: nil,
|
36
|
+
debug: false,
|
37
|
+
script: nil
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
@config.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](key)
|
46
|
+
@config[key]
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
## Set a config option
|
51
|
+
##
|
52
|
+
## @param key [String,Symbol] key
|
53
|
+
## @param value [String] value
|
54
|
+
##
|
55
|
+
def []=(key, value)
|
56
|
+
@config[key.to_sym] = value
|
57
|
+
generate_accessors
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
## Generate accessors for configuration
|
63
|
+
def generate_accessors
|
64
|
+
@config.each do |k, v|
|
65
|
+
define_singleton_method(k) { v } unless respond_to?(k)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
## Build a configuration from template name
|
71
|
+
##
|
72
|
+
## @param template [String] The template name
|
73
|
+
##
|
74
|
+
## @return [Hash] Configuration object
|
75
|
+
##
|
76
|
+
## @api private
|
77
|
+
##
|
78
|
+
def load_template
|
79
|
+
Planter.variables ||= {}
|
80
|
+
FileUtils.mkdir_p(Planter.base_dir) unless File.directory?(Planter.base_dir)
|
81
|
+
base_config = File.join(Planter.base_dir, 'planter.yml')
|
82
|
+
|
83
|
+
if File.exist?(base_config)
|
84
|
+
@config = @config.deep_merge(YAML.load(IO.read(base_config)).symbolize_keys)
|
85
|
+
else
|
86
|
+
default_base_config = {
|
87
|
+
defaults: false,
|
88
|
+
git_init: false,
|
89
|
+
files: { '_planter.yml' => 'ignore' },
|
90
|
+
color: true,
|
91
|
+
preserve_tags: true
|
92
|
+
}
|
93
|
+
begin
|
94
|
+
File.open(base_config, 'w') { |f| f.puts(YAML.dump(default_base_config.stringify_keys)) }
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
Planter.notify("Unable to create #{base_config}", :error)
|
97
|
+
end
|
98
|
+
@config = @config.deep_merge(default_base_config).symbolize_keys
|
99
|
+
Planter.notify("New configuration written to #{base_config}, edit as needed.", :warn)
|
100
|
+
end
|
101
|
+
|
102
|
+
base_dir = File.join(Planter.base_dir, 'templates', @template)
|
103
|
+
unless File.directory?(base_dir)
|
104
|
+
notify("Template #{@template} does not exist", :error)
|
105
|
+
res = Prompt.yn('Create template directory', default_response: false)
|
106
|
+
|
107
|
+
die('Canceled') unless res
|
108
|
+
|
109
|
+
FileUtils.mkdir_p(base_dir)
|
110
|
+
end
|
111
|
+
|
112
|
+
load_template_config
|
113
|
+
|
114
|
+
config_array_to_hash(:files) if @config[:files].is_a?(Array)
|
115
|
+
config_array_to_hash(:replacements) if @config[:replacements].is_a?(Array)
|
116
|
+
rescue Psych::SyntaxError => e
|
117
|
+
die("Parse error in configuration file:\n#{e.message}", :config)
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
## Load a template-specific configuration
|
122
|
+
##
|
123
|
+
## @return [Hash] updated config object
|
124
|
+
##
|
125
|
+
## @api private
|
126
|
+
##
|
127
|
+
def load_template_config
|
128
|
+
base_dir = File.join(Planter.base_dir, 'templates', @template)
|
129
|
+
config = File.join(base_dir, '_planter.yml')
|
130
|
+
|
131
|
+
unless File.exist?(config)
|
132
|
+
default_config = {
|
133
|
+
variables: [
|
134
|
+
key: 'var_key',
|
135
|
+
prompt: 'CLI Prompt',
|
136
|
+
type: '[string, float, integer, number, date]',
|
137
|
+
value: '(optional, force value, can include variables. Empty to prompt. For date type: today, now, etc.)',
|
138
|
+
default: '(optional default value, leave empty or remove key for no default)',
|
139
|
+
min: '(optional, for number type set a minimum value)',
|
140
|
+
max: '(optional, for number type set a maximum value)'
|
141
|
+
],
|
142
|
+
git_init: false,
|
143
|
+
files: {
|
144
|
+
'*.tmp' => 'ignore',
|
145
|
+
'*.bak' => 'ignore',
|
146
|
+
'.DS_Store' => 'ignore'
|
147
|
+
}
|
148
|
+
}
|
149
|
+
FileUtils.mkdir_p(base_dir)
|
150
|
+
File.open(config, 'w') { |f| f.puts(YAML.dump(default_config.stringify_keys)) }
|
151
|
+
Planter.notify("New configuration written to #{config}, please edit.", :warn)
|
152
|
+
Process.exit 0
|
153
|
+
end
|
154
|
+
@config = @config.deep_merge(YAML.load(IO.read(config)).symbolize_keys)
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
## Convert an errant array to a hash
|
159
|
+
##
|
160
|
+
## @param key [Symbol] The key in @config to convert
|
161
|
+
##
|
162
|
+
## @api private
|
163
|
+
##
|
164
|
+
def config_array_to_hash(key)
|
165
|
+
files = {}
|
166
|
+
@config[key].each do |k|
|
167
|
+
files[k.keys.first] = k.values.first
|
168
|
+
end
|
169
|
+
@config[key] = files
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/planter/filelist.rb
CHANGED
@@ -65,8 +65,15 @@ module Planter
|
|
65
65
|
apply_tags(entry)
|
66
66
|
end
|
67
67
|
|
68
|
+
#
|
69
|
+
# Apply tags to the target file from the source file
|
70
|
+
#
|
71
|
+
# @param [FileEntry] entry
|
72
|
+
#
|
73
|
+
# @return [Boolean] success
|
74
|
+
#
|
68
75
|
def apply_tags(entry)
|
69
|
-
return unless Planter.config
|
76
|
+
return unless Planter.config.preserve_tags
|
70
77
|
|
71
78
|
Tag.copy(entry.file, entry.target) if File.exist?(entry.target)
|
72
79
|
end
|
@@ -158,6 +165,9 @@ module Planter
|
|
158
165
|
## @return [Boolean] success
|
159
166
|
##
|
160
167
|
def copy_file(file, overwrite: false)
|
168
|
+
# If the target file already exists and overwrite is true,
|
169
|
+
# or Planter.overwrite is true, then delete the target file
|
170
|
+
FileUtils.rm_rf(file.target) if (overwrite || Planter.overwrite) && File.exist?(file.target)
|
161
171
|
# Check if the target file already exists
|
162
172
|
# If it does and overwrite is true, or Planter.overwrite is true,
|
163
173
|
# or if the file doesn't exist, then copy the file
|
@@ -165,7 +175,7 @@ module Planter
|
|
165
175
|
# Make sure the target directory exists
|
166
176
|
FileUtils.mkdir_p(File.dirname(file.target))
|
167
177
|
# Copy the file if it isn't a directory
|
168
|
-
FileUtils.
|
178
|
+
FileUtils.cp_r(file.file, file.target) unless File.directory?(file.file)
|
169
179
|
# Log a message to the console
|
170
180
|
Planter.notify("[Copied] #{file.file} => #{file.target}", :debug, above_spinner: true)
|
171
181
|
# Return true to indicate success
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
class ::Integer
|
5
|
+
def clean_value
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_selector?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def highlight_character(default: nil)
|
14
|
+
"(#{self})".highlight_character(default: default)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ::Float
|
19
|
+
def clean_value
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_selector?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def highlight_character(default: nil)
|
28
|
+
"(#{self})".highlight_character(default: default)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|