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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06c272d8c938a24e24d8ad5ec220ba9100f6f35fbe6e8d9765a33c982607dbd0
4
- data.tar.gz: f9a21781e985fce37c419fcf5f4e6f33cd13c951cd882468b6c90e3a19b50fdd
3
+ metadata.gz: ff00e3dd5814379c26e559c2183c04907d1082a64d4545af978192e59cb3139e
4
+ data.tar.gz: 97de83aec4990751e0e1ec91213f5ff18a87e98f073726cec84834599ebaf66a
5
5
  SHA512:
6
- metadata.gz: ffbd475e7f56a2c96cf325cd1a681e9a7541d191c9cc9f669c06e2b3a74b4c889b2256290c5b390ea36aa408bc5e262f605ed0106e4ec409b2dece751adbbcbc
7
- data.tar.gz: cc9241c6c6ac2a10ac2d5b82cdf2e6c204472c18834b113499af57e18615706a429c2900d19bdb9aa287c2d77bbe0f0a0a22c29288c10fceb292d623a23c86c3
6
+ metadata.gz: 0ececba941990543bca716162fc14ab187e72a8b330100763448e4170b4229848c7d9dae967a9ac0e75d88d6197e141f6db8bd89e528dc6beab561e83dafc64e
7
+ data.tar.gz: 0030f70d32826d28478f880ed7b17e51d8bb1312830815e27cfd050475bd720a759393e1ed3c17de618f41b7b7e14cfe47e2ad8a863eb52bdab1732636a37b49
data/.irbrc CHANGED
@@ -3,7 +3,8 @@ IRB.conf[:AUTO_INDENT] = true
3
3
 
4
4
  require "irb/completion"
5
5
  require_relative "lib/planter"
6
-
6
+ ENV['RUBYOPT'] = "-W1"
7
+ ENV['PLANTER_IRB'] = "true"
7
8
  # rubocop:disable Style/MixinUsage
8
9
  include Planter # standard:disable all
9
10
  # rubocop:enable Style/MixinUsage
data/.yardopts CHANGED
@@ -1,6 +1,7 @@
1
1
  --markup=markdown
2
2
  --private
3
3
  --protected
4
+ --list-undoc
4
5
  lib/**/*.rb
5
6
  -
6
7
  README.md
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
- # value: (force value, string can include %%variables%% and regexes will be replaced. For date type can be today, time, now, etc.)
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
- The key should be an existing key defined in `variables`. The operator can be any of:
135
+ `value:` and `default:` can include previous variables, with modifiers:
127
136
 
128
- - `==` or `=` (equals)
129
- - `=~` (matches regex)
130
- - `*=` (contains)
131
- - `^=` (starts with)
132
- - `$=` (ends with)
133
- - `>` (greater than)
134
- - `>=` (greater than or equal)
135
- - `<` (less than)
136
- - `<=` (less than or equal)
137
-
138
- The value after the operator doesn't need to be quoted, anything after the operator will be compared to the value of the key.
139
-
140
- Logic can be used on multiple lines like the example above, or on a single line (useful for filenames):
141
-
142
- ```
143
- %%project%%.%%if language == javascript%%js%%else if language == ruby%%rb%%else%%sh%%endif%%
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
- Content within if/else blocks can contain variables.
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 memu 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.
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.config = template
83
- Planter.config[:variables].sort_by { |v| v[:key].to_var }.each do |var|
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
- raise Planter::Errors::ArgumentError.new 'Template argument required'
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.config = template
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.gsub(/(?<!\\)\{(\w+)\}/i, '') unless Color.coloring?
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
@@ -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[:preserve_tags]
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.cp(file.file, file.target) unless File.directory?(file.file)
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