planter-cli 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+
6
+ describe Planter::Script do
7
+ let(:template_dir) { File.expand_path('spec/templates/test') }
8
+ let(:output_dir) { File.expand_path('spec/test_out') }
9
+ let(:script_name) { 'test.sh' }
10
+ let(:script_name_fail) { 'test_fail.sh' }
11
+ let(:script_path) { File.join(template_dir, '_scripts', script_name) }
12
+ let(:base_script_path) { File.join(Planter.base_dir, 'scripts', script_name) }
13
+
14
+ before do
15
+ Planter.base_dir = File.expand_path('spec')
16
+ allow(File).to receive(:exist?).and_call_original
17
+ allow(File).to receive(:directory?).and_call_original
18
+ allow(File).to receive(:exist?).with(script_path).and_return(true)
19
+ allow(File).to receive(:exist?).with(base_script_path).and_return(false)
20
+ allow(File).to receive(:directory?).with(output_dir).and_return(true)
21
+ end
22
+
23
+ describe '#initialize' do
24
+ it 'initializes with valid script and directories' do
25
+ script = Planter::Script.new(template_dir, output_dir, script_name)
26
+ expect(script.script).to eq(script_path)
27
+ end
28
+
29
+ it 'raises an error if script is not found' do
30
+ allow(File).to receive(:exist?).with(script_path).and_return(false)
31
+ expect do
32
+ Planter::Script.new(template_dir, output_dir, script_name)
33
+ end.to raise_error(ScriptError)
34
+ end
35
+
36
+ it 'raises an error if output directory is not found' do
37
+ allow(File).to receive(:directory?).with(output_dir).and_return(false)
38
+ expect do
39
+ Planter::Script.new(template_dir, output_dir, script_name)
40
+ end.to raise_error(ScriptError)
41
+ end
42
+ end
43
+
44
+ describe '#find_script' do
45
+ it 'finds the script in the template directory' do
46
+ script = Planter::Script.new(template_dir, output_dir, script_name)
47
+ expect(script.find_script(template_dir, script_name)).to eq(script_path)
48
+ end
49
+
50
+ it 'finds the script in the base directory' do
51
+ allow(File).to receive(:exist?).with(script_path).and_return(false)
52
+ allow(File).to receive(:exist?).with(base_script_path).and_return(true)
53
+ script = Planter::Script.new(template_dir, output_dir, script_name)
54
+ expect(script.find_script(template_dir, script_name)).to eq(base_script_path)
55
+ end
56
+
57
+ it 'returns nil if script is not found' do
58
+ allow(File).to receive(:exist?).with(script_path).and_return(false)
59
+ allow(File).to receive(:exist?).with(base_script_path).and_return(false)
60
+ expect do
61
+ script = Planter::Script.new(template_dir, output_dir, script_name)
62
+ script.find_script(template_dir, script_name)
63
+ end.to raise_error(ScriptError)
64
+ end
65
+ end
66
+
67
+ describe '#run' do
68
+ it 'executes the script successfully' do
69
+ script = Planter::Script.new(template_dir, output_dir, script_name)
70
+ expect(script.run).to be true
71
+ end
72
+
73
+ it 'raises an error if script execution fails' do
74
+ script = Planter::Script.new(template_dir, output_dir, script_name_fail)
75
+ expect do
76
+ script.run
77
+ end.to raise_error(ScriptError)
78
+ end
79
+ end
80
+ end
@@ -9,12 +9,225 @@ describe ::String do
9
9
 
10
10
  describe '.to_slug' do
11
11
  it 'slugifies a string' do
12
- expect('This is a test string'.to_slug).to match /this-is-a-test-string/
12
+ expect('This is a test string'.to_slug).to match(/this-is-a-test-string/)
13
13
  end
14
14
 
15
15
  it 'slugifies bad characters' do
16
- expect('This: #is a test string!'.to_slug).to match /this-colon-hash-is-a-test-string-bang/
16
+ expect('This: #is a test string!'.to_slug).to match(/this-colon-hash-is-a-test-string-bang/)
17
17
  end
18
18
  end
19
19
 
20
+ describe '.to_class_name' do
21
+ it 'converts string to CamelCase' do
22
+ expect('this is a test string'.to_class_name).to eq 'ThisIsATestString'
23
+ end
24
+
25
+ it 'handles special characters' do
26
+ expect('this: #is a test string!'.to_class_name).to eq 'ThisIsATestString'
27
+ end
28
+ end
29
+
30
+ describe '.snake_case' do
31
+ it 'converts CamelCase to snake_case' do
32
+ expect('ThisIsATestString'.snake_case).to eq 'this_is_a_test_string'
33
+ end
34
+
35
+ it 'handles strings with spaces' do
36
+ expect('This is a test string'.snake_case).to eq 'this_is_a_test_string'
37
+ end
38
+
39
+ it 'handles strings with special characters' do
40
+ expect('This: #is a test string!'.snake_case).to eq 'this_is_a_test_string'
41
+ end
42
+ end
43
+
44
+ describe '.camel_case' do
45
+ it 'converts snake_case to CamelCase' do
46
+ expect('this_is_a_test_string'.camel_case).to eq 'thisIsATestString'
47
+ end
48
+
49
+ it 'handles strings with spaces' do
50
+ expect('this is a test string'.camel_case).to eq 'thisIsATestString'
51
+ end
52
+
53
+ it 'handles strings with special characters' do
54
+ expect('this: #is a test string!'.camel_case).to eq 'thisIsATestString'
55
+ end
56
+ end
57
+
58
+ describe '.title_case' do
59
+ it 'converts a string to Title Case' do
60
+ expect('this is a test string'.title_case).to eq 'This Is A Test String'
61
+ end
62
+
63
+ it 'handles strings with special characters' do
64
+ expect('this: #is a test string!'.title_case).to eq 'This: #Is A Test String!'
65
+ end
66
+
67
+ it 'handles mixed case strings' do
68
+ expect('ThIs Is A TeSt StRiNg'.title_case).to eq 'This Is A Test String'
69
+ end
70
+ end
71
+
72
+ describe '.apply_variables' do
73
+ it 'replaces placeholders with variable values' do
74
+ template = 'Hello, %%name%%!'
75
+ variables = { name: 'World' }
76
+ expect(template.apply_variables(variables: variables)).to eq 'Hello, World!'
77
+ end
78
+
79
+ it 'handles multiple variables' do
80
+ template = 'Hello, %%first_name%% %%last_name%%!'
81
+ variables = { first_name: 'John', last_name: 'Doe' }
82
+ expect(template.apply_variables(variables: variables)).to eq 'Hello, John Doe!'
83
+ end
84
+
85
+ it 'handles missing variables gracefully' do
86
+ template = 'Hello, %%name%%!'
87
+ variables = {}
88
+ expect(template.apply_variables(variables: variables)).to eq 'Hello, %%name%%!'
89
+ end
90
+
91
+ it 'handles variables with special characters' do
92
+ template = 'Hello, %%name%%!'
93
+ variables = { name: 'John #Doe' }
94
+ expect(template.apply_variables(variables: variables)).to eq 'Hello, John #Doe!'
95
+ end
96
+
97
+ it 'handles modifiers' do
98
+ template = 'Hello, %%title:upper%% %%name:title%%!'
99
+ variables = { title: 'Mr.', name: 'john do' }
100
+ expect(template.apply_variables(variables: variables)).to eq 'Hello, MR. John Do!'
101
+ end
102
+
103
+ it 'operates in place' do
104
+ template = 'Hello, %%title:upper%% %%name:title%%!'
105
+ variables = { title: 'Mr.', name: 'john do' }
106
+ template.apply_variables!(variables: variables)
107
+ expect(template).to eq 'Hello, MR. John Do!'
108
+ end
109
+
110
+ it 'handles last_only' do
111
+ template = 'Hello, %%title%% %%title:upper%%!'
112
+ variables = { title: 'project title' }
113
+ expect(template.apply_variables(variables: variables, last_only: true)).to eq 'Hello, %%title%% PROJECT TITLE!'
114
+ end
115
+ end
116
+
117
+ describe '.apply_regexes' do
118
+ it 'applies a single regex replacement' do
119
+ template = 'Hello, World!'
120
+ regexes = { /World/ => 'Universe' }
121
+ expect(template.apply_regexes(regexes)).to eq 'Hello, Universe!'
122
+ end
123
+
124
+ it 'applies multiple regex replacements' do
125
+ template = 'Hello, World! Welcome to the World!'
126
+ regexes = { /World/ => 'Universe', /Welcome/ => 'Greetings' }
127
+ expect(template.apply_regexes(regexes)).to eq 'Hello, Universe! Greetings to the Universe!'
128
+ end
129
+
130
+ it 'handles no regex replacements' do
131
+ template = 'Hello, World!'
132
+ regexes = {}
133
+ expect(template.apply_regexes(regexes)).to eq 'Hello, World!'
134
+ end
135
+
136
+ it 'handles special characters in regex' do
137
+ template = 'Hello, World! #Welcome to the World!'
138
+ regexes = { /#Welcome/ => 'Greetings' }
139
+ expect(template.apply_regexes(regexes)).to eq 'Hello, World! Greetings to the World!'
140
+ end
141
+
142
+ it 'Operates in place' do
143
+ template = 'Hello, World! #Welcome to the World!'
144
+ regexes = { /#Welcome/ => 'Greetings' }
145
+ template.apply_regexes!(regexes)
146
+ expect(template).to eq 'Hello, World! Greetings to the World!'
147
+ end
148
+ end
149
+
150
+ describe '.ext' do
151
+ it 'applies an extension' do
152
+ expect("filename.rb".ext('txt')).to eq 'filename.txt'
153
+ end
154
+
155
+ it 'ignores an existing extension' do
156
+ expect("filename.rb".ext('rb')).to eq 'filename.rb'
157
+ end
158
+ end
159
+
160
+ describe '.normalize_type' do
161
+ it 'normalizes a date type' do
162
+ expect("da".normalize_type.to_s).to eq "date"
163
+ end
164
+
165
+ it 'normalizes an integer type' do
166
+ expect("int".normalize_type.to_s).to eq "integer"
167
+ end
168
+
169
+ it 'normalizes a float type' do
170
+ expect("f".normalize_type.to_s).to eq "float"
171
+ end
172
+
173
+ it 'normalizes a multiline type' do
174
+ expect("multi".normalize_type.to_s).to eq "multiline"
175
+ end
176
+
177
+ it 'normalizes a class type' do
178
+ expect("c".normalize_type.to_s).to eq "class"
179
+ end
180
+
181
+ it 'normalizes a module type' do
182
+ expect("m".normalize_type.to_s).to eq "module"
183
+ end
184
+ end
185
+
186
+ describe '.normalize_operator' do
187
+ it 'normalizes a copy operator' do
188
+ expect("copy".normalize_operator.to_s).to eq "copy"
189
+ end
190
+ end
191
+
192
+ describe '.coerce' do
193
+ it 'coerces a date type' do
194
+ expect("now".coerce(:date)).to match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/)
195
+ end
196
+
197
+ it 'coerces an integer type' do
198
+ expect("10".coerce(:integer)).to eq 10
199
+ end
200
+
201
+ it 'coerces a float type' do
202
+ expect("10.0".coerce(:float)).to eq 10.0
203
+ end
204
+
205
+ it 'coerces a multiline type' do
206
+ expect("multi\nline".coerce(:multiline)).to eq "multi\nline"
207
+ end
208
+
209
+ it 'coerces a class type' do
210
+ expect("Some class".coerce(:class)).to eq "SomeClass"
211
+ end
212
+ end
213
+
214
+ describe '.clean_encode!' do
215
+ it 'cleans an encoded string' do
216
+ s = "This is a test string"
217
+ s.clean_encode!
218
+ expect(s).to eq "This is a test string"
219
+ end
220
+ end
221
+
222
+ describe '.highlight_character' do
223
+ it 'highlights characters' do
224
+ s = "(o)ption 1 (s)econd option"
225
+ expect(s.highlight_character).to eq "{dw}({xbw}o{dw}){xw}ption 1 {dw}({xbw}s{dw}){xw}econd option"
226
+ end
227
+
228
+ it 'highlights characters with default option' do
229
+ s = "(o)ption 1 (s)econd option"
230
+ expect(s.highlight_character(default: 's')).to eq "{dw}({xbw}o{dw}){xw}ption 1 {dw}({xbc}s{dw}){xw}econd option"
231
+ end
232
+ end
20
233
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ::Symbol do
6
+ describe '.to_var' do
7
+ it 'turns a symbol into a string with _ instead of :' do
8
+ expect(:var_name.to_var).to eq :var_name
9
+ end
10
+ end
11
+
12
+ describe '.normalize_type' do
13
+ it 'normalizes a type symbol' do
14
+ expect(:string.normalize_type).to eq :string
15
+ end
16
+ end
17
+
18
+ describe '.normalize_operator' do
19
+ it 'normalizes an operator symbol' do
20
+ expect(:copy.normalize_operator).to eq :copy
21
+ end
22
+ end
23
+ end
data/spec/planter.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ defaults: false
3
+ git_init: false
4
+ files:
5
+ _planter.yml: ignore
6
+ color: true
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe "Planter" do
6
+ describe '.notify' do
7
+ it 'prints a warning message stderr' do
8
+ expect(Planter.notify('hello world', :warn)).to be true
9
+ end
10
+
11
+ it 'prints an info message stderr' do
12
+ expect(Planter.notify('hello world', :info)).to be true
13
+ end
14
+
15
+ it 'prints a debug message to stderr' do
16
+ Planter.debug = true
17
+ expect(Planter.notify('hello world', :debug)).to be true
18
+ end
19
+
20
+ it 'does not print a debug message to stderr' do
21
+ Planter.debug = false
22
+ expect(Planter.notify('hello world', :debug)).to be false
23
+ end
24
+
25
+ it 'prints an error message and exits' do
26
+ expect do
27
+ Planter.notify('Error', :error, exit_code: 10)
28
+ end.to raise_error(SystemExit)
29
+ end
30
+ end
31
+
32
+ describe '.config=' do
33
+ # it 'sets the config' do
34
+ # path = File.expand_path('spec/noop')
35
+ # FileUtils.mkdir_p(path)
36
+ # Planter.base_dir = File.expand_path('spec/noop')
37
+ # allow(File).to receive(:open).with(File.join(Planter.base_dir, "config.yml"), 'w')
38
+ # allow(File).to receive(:open).with(File.join(Planter.base_dir, 'templates', 'test', '_planter.yml'),
39
+ # 'w')
40
+ # Planter.config = 'test'
41
+ # expect(File.exist?('spec/noop/config.yml')).to be true
42
+ # FileUtils.remove_entry_secure(path)
43
+ # end
44
+ #
45
+ # it 'creates a new configuration file if it does not exist' do
46
+ # path = File.expand_path('spec/noop')
47
+ # FileUtils.mkdir_p(path)
48
+ # Planter.base_dir = File.expand_path('spec/noop')
49
+ # allow(File).to receive(:exist?).with(File.join(Planter.base_dir, 'config.yml')).and_return(false)
50
+ # expect(File).to receive(:open).with(File.join(Planter.base_dir, 'config.yml'), 'w')
51
+ # Planter.config = 'test'
52
+ # FileUtils.remove_entry_secure(path)
53
+ # end
54
+
55
+ # it 'creates a new template directory if it does not exist' do
56
+ # path = File.expand_path('spec/noop')
57
+ # FileUtils.mkdir_p(path)
58
+ # Planter.base_dir = File.expand_path('spec/noop')
59
+ # allow(File).to receive(:exist?).with(File.join(Planter.base_dir, 'templates', 'test',
60
+ # '_planter.yml')).and_return(false)
61
+ # allow(File).to receive(:directory?).with(File.join(Planter.base_dir, 'templates', 'test')).and_return(false)
62
+ # expect(FileUtils).to receive(:mkdir_p).with(File.join(Planter.base_dir, 'templates', 'test'))
63
+ # Planter.config = 'test'
64
+ # FileUtils.remove_entry_secure(path)
65
+ # end
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo "Oops"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ exit 128
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'simplecov'
2
+ require 'cli-test'
3
+ require 'fileutils'
2
4
 
3
5
  SimpleCov.start
4
6
 
@@ -13,8 +15,12 @@ require 'planter'
13
15
 
14
16
  RSpec.configure do |c|
15
17
  c.expect_with(:rspec) { |e| e.syntax = :expect }
16
- c.before(:each) do
18
+ c.before do
19
+ ENV["RUBYOPT"] = '-W1'
20
+ ENV['PLANTER_DEBUG'] = 'true'
21
+ Planter.base_dir = File.expand_path('spec')
17
22
  allow(FileUtils).to receive(:remove_entry_secure).with(anything)
23
+ allow(FileUtils).to receive(:mkdir_p).with(anything)
18
24
  end
19
- c.add_formatter 'Fuubar'
25
+ c.add_formatter 'd'
20
26
  end
@@ -0,0 +1,12 @@
1
+ ---
2
+ variables:
3
+ - key: var_key
4
+ prompt: CLI Prompt
5
+ type: "[string, float, integer, number, date]"
6
+ value: "(optional, for date type can be today, time, now, etc., empty to prompt)"
7
+ default: "(optional default value, leave empty or remove key for no default)"
8
+ min: "(optional, for number type set a minimum value)"
9
+ max: "(optional, for number type set a maximum value)"
10
+ git_init: false
11
+ files:
12
+ "*.tmp": ignore
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo "Oops"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ exit 128
Binary file
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ def test
4
+ puts "test"
5
+ end
data/src/_README.md CHANGED
@@ -19,7 +19,7 @@ If [Gum](https://github.com/charmbracelet/gum) is available it will be used for
19
19
 
20
20
  ## Configuration
21
21
 
22
- Planter's base configuration is in `~/.config/planter/config.yml`. This file can contain any of the keys used in templates (see below) and will serve as a base configuration for all templates. Any key defined in this file will be overridden if it exists in a template.
22
+ Planter's base configuration is in `~/.config/planter/planter.yml`. This file can contain any of the keys used in templates (see below) and will serve as a base configuration for all templates. Any key defined in this file will be overridden if it exists in a template.
23
23
 
24
24
  ### Scripts.
25
25
 
@@ -54,6 +54,10 @@ replacements: # Dictionary of pattern/replacments for regex substitution, see [R
54
54
  repo: # If a repository URL is provided, it will be pulled and duplicated instead of copying a file structure
55
55
  ```
56
56
 
57
+ #### Default values in template strings
58
+
59
+ 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. 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%%`.
60
+
57
61
  ### File-specific handling
58
62
 
59
63
  A `files` dictionary can specify how to handle specific files. Options are `copy`, `overwrite`, `merge`, or `ask`. The key for each entry is a filename or glob that matches the source filename (accounting for template variables if applicable):
@@ -72,6 +76,14 @@ Merged content
72
76
  // /merge
73
77
  ```
74
78
 
79
+ Or
80
+
81
+ ```
82
+ # merge
83
+ Merged content
84
+ # /merge
85
+ ```
86
+
75
87
  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.
76
88
 
77
89
  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.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: planter-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-28 00:00:00.000000000 Z
11
+ date: 2024-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bump
@@ -366,9 +366,26 @@ files:
366
366
  - scripts/deploy.rb
367
367
  - scripts/runtests.sh
368
368
  - spec/.rubocop.yml
369
+ - spec/config.yml
370
+ - spec/planter.yml
371
+ - spec/planter/array_spec.rb
372
+ - spec/planter/file_entry_spec.rb
373
+ - spec/planter/file_spec.rb
374
+ - spec/planter/filelist_spec.rb
375
+ - spec/planter/hash_spec.rb
369
376
  - spec/planter/plant_spec.rb
377
+ - spec/planter/script_spec.rb
370
378
  - spec/planter/string_spec.rb
379
+ - spec/planter/symbol_spec.rb
380
+ - spec/planter_spec.rb
381
+ - spec/scripts/test.sh
382
+ - spec/scripts/test_fail.sh
371
383
  - spec/spec_helper.rb
384
+ - spec/templates/test/_planter.yml
385
+ - spec/templates/test/_scripts/test.sh
386
+ - spec/templates/test/_scripts/test_fail.sh
387
+ - spec/test_out/image.png
388
+ - spec/test_out/test2.rb
372
389
  - src/_README.md
373
390
  homepage: https://github.com/ttscoff/planter-cli
374
391
  licenses:
@@ -389,12 +406,29 @@ required_rubygems_version: !ruby/object:Gem::Requirement
389
406
  - !ruby/object:Gem::Version
390
407
  version: '0'
391
408
  requirements: []
392
- rubygems_version: 3.2.16
409
+ rubygems_version: 3.2.15
393
410
  signing_key:
394
411
  specification_version: 4
395
412
  summary: Plant files and directories using templates
396
413
  test_files:
397
414
  - spec/.rubocop.yml
415
+ - spec/config.yml
416
+ - spec/planter.yml
417
+ - spec/planter/array_spec.rb
418
+ - spec/planter/file_entry_spec.rb
419
+ - spec/planter/file_spec.rb
420
+ - spec/planter/filelist_spec.rb
421
+ - spec/planter/hash_spec.rb
398
422
  - spec/planter/plant_spec.rb
423
+ - spec/planter/script_spec.rb
399
424
  - spec/planter/string_spec.rb
425
+ - spec/planter/symbol_spec.rb
426
+ - spec/planter_spec.rb
427
+ - spec/scripts/test.sh
428
+ - spec/scripts/test_fail.sh
400
429
  - spec/spec_helper.rb
430
+ - spec/templates/test/_planter.yml
431
+ - spec/templates/test/_scripts/test.sh
432
+ - spec/templates/test/_scripts/test_fail.sh
433
+ - spec/test_out/image.png
434
+ - spec/test_out/test2.rb