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.
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