planter-cli 3.0.4 → 3.0.5

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.
data/lib/planter.rb CHANGED
@@ -15,14 +15,15 @@ require 'tty-screen'
15
15
  require 'tty-which'
16
16
 
17
17
  require_relative 'tty-spinner/lib/tty-spinner'
18
+ require_relative 'planter/config'
18
19
  require_relative 'planter/version'
19
20
  require_relative 'planter/hash'
20
21
  require_relative 'planter/array'
21
22
  require_relative 'planter/symbol'
23
+ require_relative 'planter/numeric'
22
24
  require_relative 'planter/file'
23
25
  require_relative 'planter/tag'
24
26
  require_relative 'planter/color'
25
- require_relative 'planter/errors'
26
27
  require_relative 'planter/prompt'
27
28
  require_relative 'planter/string'
28
29
  require_relative 'planter/filelist'
@@ -30,6 +31,28 @@ require_relative 'planter/fileentry'
30
31
  require_relative 'planter/script'
31
32
  require_relative 'planter/plant'
32
33
 
34
+ # @return [Integer] Exit codes
35
+ EXIT_CODES = {
36
+ argument: 12,
37
+ input: 13,
38
+ canceled: 1,
39
+ script: 10,
40
+ config: 127,
41
+ git: 129
42
+ }.deep_freeze
43
+
44
+ #
45
+ # Exit the program with a message
46
+ #
47
+ # @param msg [String] error message
48
+ # @param level [Symbol] notification level
49
+ # @param code [Integer] Exit code
50
+ #
51
+ def die(msg = 'Exited', code = :canceled)
52
+ code = EXIT_CODES.key?(code) ? code : :canceled
53
+ Planter.notify(msg, :error, above_spinner: false, exit_code: EXIT_CODES[code])
54
+ end
55
+
33
56
  # Main Journal module
34
57
  module Planter
35
58
  # Base directory for templates
@@ -56,7 +79,7 @@ module Planter
56
79
  attr_accessor :template
57
80
 
58
81
  ## Config Hash
59
- attr_reader :config
82
+ # attr_reader :config
60
83
 
61
84
  ## Variable key/values
62
85
  attr_accessor :variables
@@ -67,6 +90,10 @@ module Planter
67
90
  ## Accept all defaults
68
91
  attr_accessor :accept_defaults
69
92
 
93
+ def config
94
+ @config ||= Config.new
95
+ end
96
+
70
97
  ##
71
98
  ## Print a message on the command line
72
99
  ##
@@ -92,11 +119,17 @@ module Planter
92
119
  '{bw}'
93
120
  end
94
121
  out = "#{color}#{string}{x}"
95
- out = out.gsub(/\[(.*?)\]/, "{by}\\1{x}#{color}")
122
+ # out = out.gsub(/\[(.*?)\]/, "{by}\\1{x}#{color}")
96
123
  out = "\n#{out}" if newline
124
+
125
+ spinner.update(title: 'ERROR') if exit_code
126
+ spinner.error if notification_type == :error
127
+
97
128
  above_spinner ? spinner.log(out.x) : warn(out.x)
98
129
 
99
- Process.exit exit_code unless exit_code.nil?
130
+ if exit_code && $stdout.isatty && (ENV['PLANTER_RSPEC'] == 'true' || ENV['PLANTER_DEBUG'] != 'true')
131
+ exit(exit_code)
132
+ end
100
133
 
101
134
  true
102
135
  end
@@ -118,56 +151,6 @@ module Planter
118
151
  @base_dir ||= ENV['PLANTER_BASE_DIR'] || File.join(Dir.home, '.config', 'planter')
119
152
  end
120
153
 
121
- ##
122
- ## Build a configuration from template name
123
- ##
124
- ## @param template [String] The template name
125
- ##
126
- ## @return [Hash] Configuration object
127
- ##
128
- def config=(template)
129
- @template = template
130
- Planter.variables ||= {}
131
- FileUtils.mkdir_p(Planter.base_dir) unless File.directory?(Planter.base_dir)
132
- base_config = File.join(Planter.base_dir, 'planter.yml')
133
-
134
- if File.exist?(base_config)
135
- @config = YAML.load(IO.read(base_config)).symbolize_keys
136
- else
137
- default_base_config = {
138
- defaults: false,
139
- git_init: false,
140
- files: { '_planter.yml' => 'ignore' },
141
- color: true,
142
- preserve_tags: true
143
- }
144
- begin
145
- File.open(base_config, 'w') { |f| f.puts(YAML.dump(default_base_config.stringify_keys)) }
146
- rescue Errno::ENOENT
147
- Planter.notify("Unable to create #{base_config}", :error)
148
- end
149
- @config = default_base_config.symbolize_keys
150
- Planter.notify("New configuration written to #{base_config}, edit as needed.", :warn)
151
- end
152
-
153
- base_dir = File.join(Planter.base_dir, 'templates', @template)
154
- unless File.directory?(base_dir)
155
- notify("Template #{@template} does not exist", :error)
156
- res = Prompt.yn('Create template directory', default_response: false)
157
-
158
- raise Errors::InputError.new('Canceled') unless res
159
-
160
- FileUtils.mkdir_p(base_dir)
161
- end
162
-
163
- load_template_config
164
-
165
- config_array_to_hash(:files) if @config[:files].is_a?(Array)
166
- config_array_to_hash(:replacements) if @config[:replacements].is_a?(Array)
167
- rescue Psych::SyntaxError => e
168
- raise Errors::ConfigError.new "Parse error in configuration file:\n#{e.message}"
169
- end
170
-
171
154
  ##
172
155
  ## Execute a shell command and return a Boolean success response
173
156
  ##
@@ -189,58 +172,6 @@ module Planter
189
172
 
190
173
  private
191
174
 
192
- ##
193
- ## Load a template-specific configuration
194
- ##
195
- ## @return [Hash] updated config object
196
- ##
197
- ## @api private
198
- ##
199
- def load_template_config
200
- base_dir = File.join(Planter.base_dir, 'templates', @template)
201
- config = File.join(base_dir, '_planter.yml')
202
-
203
- unless File.exist?(config)
204
- default_config = {
205
- variables: [
206
- key: 'var_key',
207
- prompt: 'CLI Prompt',
208
- type: '[string, float, integer, number, date]',
209
- value: '(optional, force value, can include variables. Empty to prompt. For date type: today, now, etc.)',
210
- default: '(optional default value, leave empty or remove key for no default)',
211
- min: '(optional, for number type set a minimum value)',
212
- max: '(optional, for number type set a maximum value)'
213
- ],
214
- git_init: false,
215
- files: {
216
- '*.tmp' => 'ignore',
217
- '*.bak' => 'ignore',
218
- '.DS_Store' => 'ignore'
219
- }
220
- }
221
- FileUtils.mkdir_p(base_dir)
222
- File.open(config, 'w') { |f| f.puts(YAML.dump(default_config.stringify_keys)) }
223
- notify("New configuration written to #{config}, please edit.", :warn)
224
- Process.exit 0
225
- end
226
- @config = @config.deep_merge(YAML.load(IO.read(config)).symbolize_keys)
227
- end
228
-
229
- ##
230
- ## Convert an errant array to a hash
231
- ##
232
- ## @param key [Symbol] The key in @config to convert
233
- ##
234
- ## @api private
235
- ##
236
- def config_array_to_hash(key)
237
- files = {}
238
- @config[key].each do |k, v|
239
- files[k] = v
240
- end
241
- @config[key] = files
242
- end
243
-
244
175
  ##
245
176
  ## Process :files in config into regex pattern/operator pairs
246
177
  ##
@@ -250,9 +181,9 @@ module Planter
250
181
  ##
251
182
  def process_patterns
252
183
  patterns = {}
253
- @config[:files].each do |file, oper|
184
+ @config.files.each do |file, oper|
254
185
  pattern = Regexp.new(".*?/#{file.to_s.sub(%r{^/}, '').to_rx}$")
255
- operator = oper.normalize_operator
186
+ operator = oper.apply_operator_logic(Planter.variables)
256
187
  patterns[pattern] = operator
257
188
  end
258
189
  patterns
@@ -112,7 +112,7 @@ module TTY
112
112
  pattern_or_spinner
113
113
  else
114
114
  raise ArgumentError, "Expected a pattern or spinner, " \
115
- "got: #{pattern_or_spinner.class}"
115
+ "got: #{pattern_or_spinner.class}"
116
116
  end
117
117
  end
118
118
 
@@ -259,8 +259,8 @@ module TTY
259
259
  # @api public
260
260
  def on(key, &callback)
261
261
  unless @callbacks.key?(key)
262
- raise ArgumentError, "The event #{key} does not exist. " \
263
- " Use :spin, :success, :error, or :done instead"
262
+ raise ArgumentError, "The event #{key} does not exist. " \
263
+ "Use :spin, :success, :error, or :done instead"
264
264
  end
265
265
  @callbacks[key] << callback
266
266
  self
data/planter-cli.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path(File.join('..', 'lib'), __FILE__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'planter/version'
data/spec/cli_spec.rb CHANGED
@@ -17,11 +17,21 @@ describe 'CLI' do
17
17
 
18
18
  it 'displays help message' do
19
19
  output, stderr, status = planter('--help')
20
- expect(output).not_to be_empty
20
+ expect(output).to match(/Usage: plant \[options\] TEMPLATE/)
21
+ end
22
+
23
+ it 'displays variables for a template' do
24
+ output, stderr, status = planter('--help', 'test')
25
+ expect(output).to match(/CLI Prompt/)
21
26
  end
22
27
 
23
28
  it 'plants a new project' do
24
- output, stderr, status = planter('--defaults', "--in=#{TEST_DIR}", 'test')
29
+ output, stderr, status = planter("--in=#{TEST_DIR}", 'test')
25
30
  expect(File.exist?(File.join(TEST_DIR, 'bollocks_and_beans.rtf'))).to be true
26
31
  end
32
+
33
+ it 'plants a new file with a script' do
34
+ output, stderr, status = planter("--in=#{TEST_DIR}", 'test')
35
+ expect(File.exist?(File.join(TEST_DIR, 'planted_by_script.txt'))).to be true
36
+ end
27
37
  end
@@ -7,8 +7,8 @@ describe Planter::FileList do
7
7
  it 'initializes with an empty list' do
8
8
  Planter.base_dir = File.expand_path('spec')
9
9
  Planter.variables = { project: 'Untitled', script: 'Script', title: 'Title' }
10
- Planter.config = 'test'
11
- filelist = Planter::FileList.new
10
+ Planter.template = 'test'
11
+ filelist = described_class.new
12
12
  expect(filelist.files).not_to eq([])
13
13
  end
14
14
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Planter::Prompt::Question do
6
+ let(:question) do
7
+ question = {
8
+ key: 'test',
9
+ prompt: 'CLI Prompt',
10
+ type: :string,
11
+ default: 'default',
12
+ value: nil
13
+ }
14
+ end
15
+
16
+ before do
17
+ Planter.accept_defaults = true
18
+ end
19
+
20
+ describe '#initialize' do
21
+ it 'initializes a new question object' do
22
+ q = described_class.new(question)
23
+ expect(q).to be_a described_class
24
+ end
25
+ end
26
+
27
+ describe '#ask' do
28
+ it 'asks a question' do
29
+ q = described_class.new(question)
30
+ expect(q.ask).to eq('default')
31
+ end
32
+ end
33
+
34
+ describe "#ask with date type" do
35
+ it 'asks a date question' do
36
+ question[:type] = :date
37
+ question[:value] = 'today'
38
+ q = described_class.new(question)
39
+ expect(q.ask).to eq(Date.today.strftime('%Y-%m-%d'))
40
+ end
41
+ end
42
+
43
+ describe "#ask with date type and inline date format" do
44
+ it 'asks a date question' do
45
+ question[:type] = :date
46
+ question[:value] = "today '%Y'"
47
+ q = described_class.new(question)
48
+ expect(q.ask).to eq(Date.today.strftime('%Y'))
49
+ end
50
+ end
51
+
52
+ describe "#ask with date type and date format config" do
53
+ it 'asks a date question' do
54
+ question[:type] = :date
55
+ question[:date_format] = '%Y'
56
+ question[:value] = "today"
57
+ q = described_class.new(question)
58
+ expect(q.ask).to eq(Date.today.strftime('%Y'))
59
+ end
60
+ end
61
+
62
+ describe "#ask with choices" do
63
+ it 'asks a question with choices' do
64
+ question[:type] = :string
65
+ question[:choices] = %w[(o)ne (t)wo t(h)ree]
66
+ question[:default] = 'one'
67
+ q = described_class.new(question)
68
+ expect(q.ask).to eq('one')
69
+ end
70
+ end
71
+ end
@@ -12,6 +12,7 @@ describe Planter::Script do
12
12
  let(:base_script_path) { File.join(Planter.base_dir, 'scripts', script_name) }
13
13
 
14
14
  before do
15
+ ENV['PLANTER_RSPEC'] = 'true'
15
16
  Planter.base_dir = File.expand_path('spec')
16
17
  allow(File).to receive(:exist?).and_call_original
17
18
  allow(File).to receive(:directory?).and_call_original
@@ -30,14 +31,14 @@ describe Planter::Script do
30
31
  allow(File).to receive(:exist?).with(script_path).and_return(false)
31
32
  expect do
32
33
  Planter::Script.new(template_dir, output_dir, script_name)
33
- end.to raise_error(ScriptError)
34
+ end.to raise_error(SystemExit)
34
35
  end
35
36
 
36
37
  it 'raises an error if output directory is not found' do
37
38
  allow(File).to receive(:directory?).with(output_dir).and_return(false)
38
39
  expect do
39
40
  Planter::Script.new(template_dir, output_dir, script_name)
40
- end.to raise_error(ScriptError)
41
+ end.to raise_error(SystemExit)
41
42
  end
42
43
  end
43
44
 
@@ -60,7 +61,7 @@ describe Planter::Script do
60
61
  expect do
61
62
  script = Planter::Script.new(template_dir, output_dir, script_name)
62
63
  script.find_script(template_dir, script_name)
63
- end.to raise_error(ScriptError)
64
+ end.to raise_error(SystemExit)
64
65
  end
65
66
  end
66
67
 
@@ -74,7 +75,7 @@ describe Planter::Script do
74
75
  script = Planter::Script.new(template_dir, output_dir, script_name_fail)
75
76
  expect do
76
77
  script.run
77
- end.to raise_error(ScriptError)
78
+ end.to raise_error(SystemExit)
78
79
  end
79
80
  end
80
81
  end
@@ -141,6 +141,32 @@ describe ::String do
141
141
  end
142
142
  end
143
143
 
144
+ describe '.apply_operator_logic' do
145
+ it 'applies logic' do
146
+ template = 'copy if language == ruby'
147
+ operators = { language: 'ruby' }
148
+ expect(template.apply_operator_logic(operators)).to eq :copy
149
+ end
150
+
151
+ it 'applies default' do
152
+ template = 'copy if language == perl'
153
+ operators = { language: 'ruby' }
154
+ expect(template.apply_operator_logic(operators)).to eq :ignore
155
+ end
156
+
157
+ it 'handles no logic' do
158
+ template = 'copy'
159
+ operators = {}
160
+ expect(template.apply_operator_logic(operators)).to eq :copy
161
+ end
162
+
163
+ it 'handles if then' do
164
+ template = 'if language == ruby: copy; else: ignore'
165
+ operators = { language: 'perl' }
166
+ expect(template.apply_operator_logic(operators)).to eq :ignore
167
+ end
168
+ end
169
+
144
170
  describe '.apply_regexes' do
145
171
  it 'applies a single regex replacement' do
146
172
  template = 'Hello, World!'
data/spec/spec_helper.rb CHANGED
@@ -29,10 +29,11 @@ require 'open3'
29
29
  require 'time'
30
30
 
31
31
  module PlanterHelpers
32
- PLANTER_EXEC = File.join(File.dirname(__FILE__), '..', 'exe', 'plant')
32
+ PLANTER_EXEC = File.join(File.dirname(__FILE__), '..', 'bin', 'plant')
33
33
 
34
34
  def planter_with_env(env, *args, stdin: nil)
35
- pread(env, 'bundle', 'exec', PLANTER_EXEC, "--base-dir=#{File.dirname(__FILE__)}", *args, stdin: stdin)
35
+ pread(env, 'bundle', 'exec', PLANTER_EXEC, "--base-dir=#{File.dirname(__FILE__)}", "--defaults", *args,
36
+ stdin: stdin)
36
37
  end
37
38
 
38
39
  def pread(env, *cmd, stdin: nil)
@@ -5,5 +5,7 @@ variables:
5
5
  type: string
6
6
  default: "bollocks and beans"
7
7
  git_init: false
8
+ script:
9
+ - plant.sh
8
10
  files:
9
11
  "*.tmp": ignore
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ echo "Testing" > planted_by_script.txt