planter-cli 3.0.4 → 3.0.7

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: 9d14e865eb5a5c296dc5b5672ffbacece33956b90e87c796b4d773f8c8ad1fa3
4
+ data.tar.gz: 8b1a869c83c2c0a31692cd41a24044d3b90e9558da5911de6e7549c60204bbc5
5
5
  SHA512:
6
- metadata.gz: ffbd475e7f56a2c96cf325cd1a681e9a7541d191c9cc9f669c06e2b3a74b4c889b2256290c5b390ea36aa408bc5e262f605ed0106e4ec409b2dece751adbbcbc
7
- data.tar.gz: cc9241c6c6ac2a10ac2d5b82cdf2e6c204472c18834b113499af57e18615706a429c2900d19bdb9aa287c2d77bbe0f0a0a22c29288c10fceb292d623a23c86c3
6
+ metadata.gz: ade318397e46f22c542775777285f967848dd4d4271452dad8b15f6224ef99923cffb8558de4efb8ce1161d7c1398d260d1864d4923be3f8a17f70a46835160c
7
+ data.tar.gz: 2ce7038f68dad0af81a595ceb7bd7efc7487a87d9c2d4df86bade005c109efdfa00ef139298dce03aa1e3d074ee45155d5c756f1f264dfe08d134c62e4d957a8
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/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  Style/RegexpLiteral:
2
4
  Exclude:
3
5
  - Guardfile
@@ -16,6 +18,7 @@ AllCops:
16
18
  Exclude:
17
19
  - pkg/**/*.rb
18
20
  - test/*
21
+ - 'lib/tty-spinner/**/*'
19
22
 
20
23
  Style/MutableConstant:
21
24
  Enabled: false
@@ -31,9 +34,6 @@ Style/StringLiteralsInInterpolation:
31
34
  Enabled: true
32
35
  EnforcedStyle: single_quotes
33
36
 
34
- Layout/LineLength:
35
- Max: 120
36
-
37
37
  Metrics/MethodLength:
38
38
  Max: 45
39
39
 
@@ -42,6 +42,7 @@ Metrics/BlockLength:
42
42
  Exclude:
43
43
  - Rakefile
44
44
  - lib/*.rb
45
+ - lib/planter/string.rb
45
46
 
46
47
  Metrics/ClassLength:
47
48
  Max: 300
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,63 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2024-09-04 13:26:32 UTC using RuboCop version 1.66.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
12
+ # URISchemes: http, https
13
+ Layout/LineLength:
14
+ Max: 125
15
+
16
+ # Offense count: 1
17
+ # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
18
+ # AllowedMethods: refine
19
+ Metrics/BlockLength:
20
+ Exclude:
21
+ - 'lib/planter/string.rb'
22
+
23
+ # Offense count: 1
24
+ # Configuration parameters: CountComments, Max, CountAsOne.
25
+ Metrics/ClassLength:
26
+ Exclude:
27
+ - 'lib/planter/string.rb'
28
+
29
+ # Offense count: 2
30
+ # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
31
+ Metrics/CyclomaticComplexity:
32
+ Exclude:
33
+ - 'lib/planter/prompt.rb'
34
+ - 'lib/planter/string.rb'
35
+
36
+ # Offense count: 2
37
+ # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
38
+ Metrics/MethodLength:
39
+ Exclude:
40
+ - 'lib/planter/prompt.rb'
41
+ - 'lib/planter/string.rb'
42
+
43
+ # Offense count: 1
44
+ # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
45
+ Metrics/PerceivedComplexity:
46
+ Exclude:
47
+ - 'lib/planter/prompt.rb'
48
+
49
+ # Offense count: 1
50
+ # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
51
+ # CheckDefinitionPathHierarchyRoots: lib, spec, test, src
52
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
53
+ Naming/FileName:
54
+ Exclude:
55
+ - 'Rakefile.rb'
56
+ - 'spec/templates/test/Rakefile'
57
+
58
+ # Offense count: 1
59
+ # This cop supports safe autocorrection (--autocorrect).
60
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
61
+ # URISchemes: http, https
62
+ Layout/LineLength:
63
+ Max: 125
data/.yardopts CHANGED
@@ -1,6 +1,7 @@
1
1
  --markup=markdown
2
- --private
3
- --protected
2
+ --no-private
3
+ --list-undoc
4
+ --exclude lib/tty-spinner/
4
5
  lib/**/*.rb
5
6
  -
6
7
  README.md
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ### 3.0.6
2
+
3
+ 2024-09-05 05:53
4
+
5
+ #### IMPROVED
6
+
7
+ - Code cleanup and documentation
8
+
9
+ ### 3.0.5
10
+
11
+ 2024-09-04 07:49
12
+
13
+ #### NEW
14
+
15
+ - Date_format setting for date types
16
+ - Allow `today '%Y'` inline date formatting
17
+ - If/then logic for file operators
18
+ - First_letter, first_word modifiers for variables
19
+ - Allow default/value variable configutations to include %%placeholders%% and modifiers
20
+
21
+ #### IMPROVED
22
+
23
+ - Code refactoring
24
+
25
+ #### FIXED
26
+
27
+ - Overwrite operation not overwriting
28
+
1
29
  ### 3.0.4
2
30
 
3
31
  2024-09-02 08:43
data/Guardfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- scope groups: %i[doc lint unit]
3
+ # scope groups: %i[doc lint unit]
4
4
 
5
5
  group :doc do
6
6
  guard :yard do
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.
@@ -215,14 +302,13 @@ Some directories like `.git` and files like `_planter.yml` are automatically ign
215
302
 
216
303
  When `plant` is run, any defined variables will be requested on the command line using the defined prompt. If a `default` key is specified, hitting return at the prompt will accept the default value.
217
304
 
218
- Variables can be passed on the command line with `--var KEY:VALUE`. This flag can contain a comma-separated list, e.g. `--var KEY:VALUE,KEY:VALUE` or used multiple times in the same command. Variables passed on the command line will not be prompted for when processing variables.
305
+ Variables can be passed on the command line with `--var KEY:VALUE`. This flag can contain a comma-separated list, e.g. `--var KEY:VALUE,KEY:VALUE` or be used multiple times in the same command. Variables passed on the command line will not be prompted for when processing variables.
219
306
 
220
307
 
221
308
 
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/Rakefile CHANGED
@@ -134,9 +134,6 @@ task :cver do
134
134
  puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
135
135
  end
136
136
 
137
- desc 'Alias for build'
138
- task package: :build
139
-
140
137
  desc 'Bump incremental version number'
141
138
  task :bump, :type do |_, args|
142
139
  args.with_defaults(type: 'inc')
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
  ##
@@ -122,9 +128,7 @@ module Planter
122
128
  compiled = ''
123
129
  normalize_color.chars.each do |char|
124
130
  compiled += char
125
- if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
126
- valid_color = compiled
127
- end
131
+ valid_color = compiled if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
128
132
  end
129
133
 
130
134
  valid_color
@@ -243,7 +247,7 @@ module Planter
243
247
  ##
244
248
  def template(input)
245
249
  input = input.join(' ') if input.is_a? Array
246
- return input.gsub(/(?<!\\)\{(\w+)\}/i, '') unless Color.coloring?
250
+ return input.strip_template unless Color.coloring?
247
251
 
248
252
  input = input.gsub(/(?<!\\)\{((?:[fb]g?)?#[a-f0-9]{3,6})\}/i) do
249
253
  hex = Regexp.last_match(1)