planter-cli 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +4 -6
- data/Rakefile +2 -2
- data/bin/plant +6 -0
- data/lib/planter/array.rb +34 -0
- data/lib/planter/color.rb +1 -1
- data/lib/planter/errors.rb +14 -0
- data/lib/planter/file.rb +87 -4
- data/lib/planter/filelist.rb +35 -7
- data/lib/planter/hash.rb +65 -84
- data/lib/planter/plant.rb +2 -10
- data/lib/planter/prompt.rb +6 -3
- data/lib/planter/script.rb +24 -12
- data/lib/planter/string.rb +134 -29
- data/lib/planter/version.rb +1 -1
- data/lib/planter.rb +56 -33
- data/spec/config.yml +2 -0
- data/spec/planter/array_spec.rb +28 -0
- data/spec/planter/file_entry_spec.rb +40 -0
- data/spec/planter/file_spec.rb +19 -0
- data/spec/planter/filelist_spec.rb +15 -0
- data/spec/planter/hash_spec.rb +83 -0
- data/spec/planter/plant_spec.rb +1 -0
- data/spec/planter/script_spec.rb +80 -0
- data/spec/planter/string_spec.rb +215 -2
- data/spec/planter/symbol_spec.rb +23 -0
- data/spec/planter.yml +6 -0
- data/spec/planter_spec.rb +67 -0
- data/spec/scripts/test.sh +3 -0
- data/spec/scripts/test_fail.sh +3 -0
- data/spec/spec_helper.rb +8 -2
- data/spec/templates/test/_planter.yml +12 -0
- data/spec/templates/test/_scripts/test.sh +3 -0
- data/spec/templates/test/_scripts/test_fail.sh +3 -0
- data/spec/test_out/image.png +0 -0
- data/spec/test_out/test2.rb +5 -0
- data/src/_README.md +13 -1
- metadata +37 -3
data/lib/planter/string.rb
CHANGED
@@ -23,7 +23,7 @@ module Planter
|
|
23
23
|
## @return Class name representation of the object.
|
24
24
|
##
|
25
25
|
def to_class_name
|
26
|
-
strip.no_ext.
|
26
|
+
strip.no_ext.title_case.gsub(/[^a-z0-9]/i, '').sub(/^\S/, &:upcase)
|
27
27
|
end
|
28
28
|
|
29
29
|
##
|
@@ -66,7 +66,7 @@ module Planter
|
|
66
66
|
## @return [String] Snake-cased version of string
|
67
67
|
##
|
68
68
|
def snake_case
|
69
|
-
strip.gsub(/\S[A-Z]
|
69
|
+
strip.gsub(/\S(?=[A-Z])/, '\0_')
|
70
70
|
.gsub(/[ -]+/, '_')
|
71
71
|
.gsub(/[^a-z0-9_]+/i, '')
|
72
72
|
.gsub(/_+/, '_')
|
@@ -82,7 +82,7 @@ module Planter
|
|
82
82
|
## @return [String] Snake-cased version of string
|
83
83
|
##
|
84
84
|
def camel_case
|
85
|
-
strip.gsub(/[
|
85
|
+
strip.gsub(/(?<=[^a-z0-9])(\S)/) { Regexp.last_match(1).upcase }
|
86
86
|
.gsub(/[^a-z0-9]+/i, '')
|
87
87
|
.sub(/^(\w)/) { Regexp.last_match(1).downcase }
|
88
88
|
end
|
@@ -96,20 +96,11 @@ module Planter
|
|
96
96
|
## @return [String] title cased string
|
97
97
|
##
|
98
98
|
def title_case
|
99
|
-
|
99
|
+
split(/\b(\w+)/).map(&:capitalize).join('')
|
100
100
|
end
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
## %%key%%, and the hash passed to the function is { key: value }
|
105
|
-
##
|
106
|
-
## @param last_only [Boolean] Only replace the last instance of %%key%%
|
107
|
-
##
|
108
|
-
## @return [String] string with variables substituted
|
109
|
-
##
|
110
|
-
def apply_variables(last_only: false)
|
111
|
-
content = dup.clean_encode
|
112
|
-
mod_rx = '(?<mod>
|
102
|
+
# @return [String] Regular expression for matching variable modifiers
|
103
|
+
MOD_RX = '(?<mod>
|
113
104
|
(?::
|
114
105
|
(
|
115
106
|
l(?:ow(?:er)?)?)?|
|
@@ -121,11 +112,75 @@ module Planter
|
|
121
112
|
)?
|
122
113
|
)*
|
123
114
|
)'
|
115
|
+
# @return [String] regular expression string for default values
|
116
|
+
DEFAULT_RX = '(?:%(?<default>[^%]+))?'
|
117
|
+
|
118
|
+
#
|
119
|
+
# Apply default values to a string
|
120
|
+
#
|
121
|
+
# Default values are applied to variables that are not present in the variables hash,
|
122
|
+
# or whose value matches the default value
|
123
|
+
#
|
124
|
+
# @param variables [Hash] Hash of variable values
|
125
|
+
#
|
126
|
+
# @return [String] string with default values applied
|
127
|
+
#
|
128
|
+
def apply_defaults(variables)
|
129
|
+
# Perform an in-place substitution on the content string for default values
|
130
|
+
gsub(/%%(?<varname>[^%:]+)(?<mods>(?::[^%]+)*)%(?<default>[^%]+)%%/) do
|
131
|
+
# Capture the last match object
|
132
|
+
m = Regexp.last_match
|
133
|
+
|
134
|
+
# Check if the variable is not present in the variables hash
|
135
|
+
if !variables.key?(m['varname'].to_var)
|
136
|
+
# If the variable is not present, use the default value from the match
|
137
|
+
m['default'].apply_var_names
|
138
|
+
else
|
139
|
+
# Retrieve the default value for the variable from the configuration
|
140
|
+
vars = Planter.config[:variables].filter { |v| v[:key] == m['varname'] }
|
141
|
+
default = vars.first[:default] if vars.count.positive?
|
142
|
+
if default.nil?
|
143
|
+
m[0]
|
144
|
+
elsif variables[m['varname'].to_var] == default
|
145
|
+
# If the variable's value matches the default value, use the default value from the match
|
146
|
+
m['default'].apply_var_names
|
147
|
+
else
|
148
|
+
m[0]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
# Destructive version of #apply_defaults
|
156
|
+
#
|
157
|
+
# @param variables [Hash] hash of variables to apply
|
158
|
+
#
|
159
|
+
# @return [String] string with defaults applied
|
160
|
+
#
|
161
|
+
def apply_defaults!(variables)
|
162
|
+
replace apply_defaults(variables)
|
163
|
+
end
|
124
164
|
|
125
|
-
|
165
|
+
##
|
166
|
+
## Apply key/value substitutions to a string. Variables are represented as
|
167
|
+
## %%key%%, and the hash passed to the function is { key: value }
|
168
|
+
##
|
169
|
+
## @param last_only [Boolean] Only replace the last instance of %%key%%
|
170
|
+
##
|
171
|
+
## @return [String] string with variables substituted
|
172
|
+
##
|
173
|
+
def apply_variables(variables: nil, last_only: false)
|
174
|
+
variables = variables.nil? ? Planter.variables : variables
|
175
|
+
|
176
|
+
content = dup.clean_encode
|
177
|
+
|
178
|
+
content = content.apply_defaults(variables)
|
179
|
+
|
180
|
+
variables.each do |k, v|
|
126
181
|
if last_only
|
127
182
|
pattern = "%%#{k.to_var}"
|
128
|
-
content = content.reverse.sub(/(?mix)%%(?:(?<mod>.*?):)*(?<key>#{pattern.reverse})/) do
|
183
|
+
content = content.reverse.sub(/(?mix)%%(?:(?<mod>.*?):)*(?<key>#{pattern.reverse})/i) do
|
129
184
|
m = Regexp.last_match
|
130
185
|
if m['mod']
|
131
186
|
m['mod'].reverse.split(/:/).each do |mod|
|
@@ -136,14 +191,16 @@ module Planter
|
|
136
191
|
v.reverse
|
137
192
|
end.reverse
|
138
193
|
else
|
139
|
-
rx = /(?mix)%%(?<key>#{k.to_var})#{
|
194
|
+
rx = /(?mix)%%(?<key>#{k.to_var})#{MOD_RX}#{DEFAULT_RX}%%/
|
140
195
|
|
141
196
|
content.gsub!(rx) do
|
142
197
|
m = Regexp.last_match
|
143
198
|
|
144
|
-
|
145
|
-
|
146
|
-
|
199
|
+
if m['mod']
|
200
|
+
mods = m['mod']&.split(/:/)
|
201
|
+
mods&.each do |mod|
|
202
|
+
v = v.apply_mod(mod.normalize_mod)
|
203
|
+
end
|
147
204
|
end
|
148
205
|
v
|
149
206
|
end
|
@@ -153,16 +210,49 @@ module Planter
|
|
153
210
|
content
|
154
211
|
end
|
155
212
|
|
213
|
+
#
|
214
|
+
# Handle $varname and ${varname} variable substitutions
|
215
|
+
#
|
216
|
+
# @return [String] String with variables substituted
|
217
|
+
#
|
218
|
+
def apply_var_names
|
219
|
+
sub(/\$\{?(?<varname>\w+)(?<mods>(?::\w+)+)?\}?/) do
|
220
|
+
m = Regexp.last_match
|
221
|
+
if Planter.variables.key?(m['varname'].to_var)
|
222
|
+
Planter.variables[m['varname'].to_var].apply_mods(m['mods'])
|
223
|
+
else
|
224
|
+
m
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# Apply modifiers to a string
|
231
|
+
#
|
232
|
+
# @param mods [String] Colon separated list of modifiers to apply
|
233
|
+
#
|
234
|
+
# @return [String] string with modifiers applied
|
235
|
+
#
|
236
|
+
def apply_mods(mods)
|
237
|
+
content = dup
|
238
|
+
mods.split(/:/).each do |mod|
|
239
|
+
content.apply_mod!(mod.normalize_mod)
|
240
|
+
end
|
241
|
+
content
|
242
|
+
end
|
243
|
+
|
156
244
|
##
|
157
245
|
## Apply regex replacements from @config[:replacements]
|
158
246
|
##
|
159
247
|
## @return [String] string with regexes applied
|
160
248
|
##
|
161
|
-
def apply_regexes
|
249
|
+
def apply_regexes(regexes = nil)
|
162
250
|
content = dup.clean_encode
|
163
|
-
|
251
|
+
regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config[:replacements] : regexes
|
164
252
|
|
165
|
-
|
253
|
+
return self unless regexes
|
254
|
+
|
255
|
+
regexes.stringify_keys.each do |pattern, replacement|
|
166
256
|
pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
|
167
257
|
replacement = replacement.gsub(/\$(\d)/, '\\\1').apply_variables
|
168
258
|
content.gsub!(pattern, replacement)
|
@@ -177,8 +267,8 @@ module Planter
|
|
177
267
|
##
|
178
268
|
## @return [String] string with variables substituted
|
179
269
|
##
|
180
|
-
def apply_variables!(last_only: false)
|
181
|
-
replace apply_variables(last_only: last_only)
|
270
|
+
def apply_variables!(variables: nil, last_only: false)
|
271
|
+
replace apply_variables(variables: variables, last_only: last_only)
|
182
272
|
end
|
183
273
|
|
184
274
|
##
|
@@ -186,8 +276,8 @@ module Planter
|
|
186
276
|
##
|
187
277
|
## @return [String] string with variables substituted
|
188
278
|
##
|
189
|
-
def apply_regexes!
|
190
|
-
replace apply_regexes
|
279
|
+
def apply_regexes!(regexes = nil)
|
280
|
+
replace apply_regexes(regexes)
|
191
281
|
end
|
192
282
|
|
193
283
|
##
|
@@ -222,6 +312,8 @@ module Planter
|
|
222
312
|
##
|
223
313
|
## @param mod [Symbol] The modifier to apply
|
224
314
|
##
|
315
|
+
## @return [String] modified string
|
316
|
+
##
|
225
317
|
def apply_mod(mod)
|
226
318
|
case mod
|
227
319
|
when :slug
|
@@ -241,6 +333,17 @@ module Planter
|
|
241
333
|
end
|
242
334
|
end
|
243
335
|
|
336
|
+
#
|
337
|
+
# Destructive version of #apply_mod
|
338
|
+
#
|
339
|
+
# @param mod [String] modified string
|
340
|
+
#
|
341
|
+
# @return [<Type>] <description>
|
342
|
+
#
|
343
|
+
def apply_mod!(mod)
|
344
|
+
replace apply_mod(mod)
|
345
|
+
end
|
346
|
+
|
244
347
|
##
|
245
348
|
## Convert mod string to symbol
|
246
349
|
##
|
@@ -334,9 +437,11 @@ module Planter
|
|
334
437
|
##
|
335
438
|
##
|
336
439
|
def coerce(type)
|
440
|
+
type = type.normalize_type
|
441
|
+
|
337
442
|
case type
|
338
443
|
when :date
|
339
|
-
Chronic.parse(self)
|
444
|
+
Chronic.parse(self).strftime('%Y-%m-%d %H:%M')
|
340
445
|
when :integer || :number
|
341
446
|
to_i
|
342
447
|
when :float
|
data/lib/planter/version.rb
CHANGED
data/lib/planter.rb
CHANGED
@@ -24,17 +24,19 @@ require_relative 'planter/prompt'
|
|
24
24
|
require_relative 'planter/string'
|
25
25
|
require_relative 'planter/filelist'
|
26
26
|
require_relative 'planter/fileentry'
|
27
|
+
require_relative 'planter/script'
|
27
28
|
require_relative 'planter/plant'
|
28
29
|
|
29
30
|
# Main Journal module
|
30
31
|
module Planter
|
31
32
|
# Base directory for templates
|
32
|
-
BASE_DIR = File.expand_path('~/.config/planter/')
|
33
|
-
|
34
33
|
class << self
|
35
34
|
include Color
|
36
35
|
include Prompt
|
37
36
|
|
37
|
+
## Base directory for templates
|
38
|
+
attr_writer :base_dir
|
39
|
+
|
38
40
|
## Debug mode
|
39
41
|
attr_accessor :debug
|
40
42
|
|
@@ -72,7 +74,9 @@ module Planter
|
|
72
74
|
def notify(string, notification_type = :info, exit_code: nil)
|
73
75
|
case notification_type
|
74
76
|
when :debug
|
75
|
-
|
77
|
+
return false unless @debug
|
78
|
+
|
79
|
+
warn "\n{dw}#{string}{x}".x
|
76
80
|
when :error
|
77
81
|
warn "{br}#{string}{x}".x
|
78
82
|
when :warn
|
@@ -82,6 +86,8 @@ module Planter
|
|
82
86
|
end
|
83
87
|
|
84
88
|
Process.exit exit_code unless exit_code.nil?
|
89
|
+
|
90
|
+
true
|
85
91
|
end
|
86
92
|
|
87
93
|
##
|
@@ -97,6 +103,10 @@ module Planter
|
|
97
103
|
error_mark: '{br}✖{x}'.x)
|
98
104
|
end
|
99
105
|
|
106
|
+
def base_dir
|
107
|
+
@base_dir ||= ENV['PLANTER_BASE_DIR'] || File.join(Dir.home, '.config', 'planter')
|
108
|
+
end
|
109
|
+
|
100
110
|
##
|
101
111
|
## Build a configuration from template name
|
102
112
|
##
|
@@ -105,26 +115,30 @@ module Planter
|
|
105
115
|
## @return [Hash] Configuration object
|
106
116
|
##
|
107
117
|
def config=(template)
|
108
|
-
Planter.spinner.update(title: 'Initializing configuration')
|
109
118
|
@template = template
|
110
119
|
Planter.variables ||= {}
|
111
|
-
FileUtils.mkdir_p(
|
112
|
-
base_config = File.join(
|
120
|
+
FileUtils.mkdir_p(Planter.base_dir) unless File.directory?(Planter.base_dir)
|
121
|
+
base_config = File.join(Planter.base_dir, 'planter.yml')
|
113
122
|
|
114
|
-
|
123
|
+
if File.exist?(base_config)
|
124
|
+
@config = YAML.load(IO.read(base_config)).symbolize_keys
|
125
|
+
else
|
115
126
|
default_base_config = {
|
116
127
|
defaults: false,
|
117
128
|
git_init: false,
|
118
129
|
files: { '_planter.yml' => 'ignore' },
|
119
130
|
color: true
|
120
131
|
}
|
121
|
-
|
122
|
-
|
132
|
+
begin
|
133
|
+
File.open(base_config, 'w') { |f| f.puts(YAML.dump(default_base_config.stringify_keys)) }
|
134
|
+
rescue Errno::ENOENT
|
135
|
+
Planter.notify("Unable to create #{base_config}", :error)
|
136
|
+
end
|
137
|
+
@config = default_base_config.symbolize_keys
|
138
|
+
Planter.notify("New configuration written to #{base_config}, edit as needed.", :warn)
|
123
139
|
end
|
124
140
|
|
125
|
-
|
126
|
-
|
127
|
-
base_dir = File.join(BASE_DIR, 'templates', @template)
|
141
|
+
base_dir = File.join(Planter.base_dir, 'templates', @template)
|
128
142
|
unless File.directory?(base_dir)
|
129
143
|
notify("Template #{@template} does not exist", :error)
|
130
144
|
res = Prompt.yn('Create template directory', default_response: false)
|
@@ -142,13 +156,36 @@ module Planter
|
|
142
156
|
raise Errors::ConfigError.new "Parse error in configuration file:\n#{e.message}"
|
143
157
|
end
|
144
158
|
|
159
|
+
##
|
160
|
+
## Execute a shell command and return a Boolean success response
|
161
|
+
##
|
162
|
+
## @param cmd [String] The shell command
|
163
|
+
##
|
164
|
+
def pass_fail(cmd)
|
165
|
+
_, status = Open3.capture2("#{cmd} &> /dev/null")
|
166
|
+
status.exitstatus.zero?
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
## Patterns reader, file handling config
|
171
|
+
##
|
172
|
+
## @return [Hash] hash of file patterns
|
173
|
+
##
|
174
|
+
def patterns
|
175
|
+
@patterns ||= process_patterns
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
145
180
|
##
|
146
181
|
## Load a template-specific configuration
|
147
182
|
##
|
148
183
|
## @return [Hash] updated config object
|
149
184
|
##
|
185
|
+
## @api private
|
186
|
+
##
|
150
187
|
def load_template_config
|
151
|
-
base_dir = File.join(
|
188
|
+
base_dir = File.join(Planter.base_dir, 'templates', @template)
|
152
189
|
config = File.join(base_dir, '_planter.yml')
|
153
190
|
|
154
191
|
unless File.exist?(config)
|
@@ -165,8 +202,9 @@ module Planter
|
|
165
202
|
git_init: false,
|
166
203
|
files: { '*.tmp' => 'ignore' }
|
167
204
|
}
|
205
|
+
FileUtils.mkdir_p(base_dir)
|
168
206
|
File.open(config, 'w') { |f| f.puts(YAML.dump(default_config.stringify_keys)) }
|
169
|
-
|
207
|
+
notify("New configuration written to #{config}, please edit.", :warn)
|
170
208
|
Process.exit 0
|
171
209
|
end
|
172
210
|
@config = @config.deep_merge(YAML.load(IO.read(config)).symbolize_keys)
|
@@ -177,6 +215,8 @@ module Planter
|
|
177
215
|
##
|
178
216
|
## @param key [Symbol] The key in @config to convert
|
179
217
|
##
|
218
|
+
## @api private
|
219
|
+
##
|
180
220
|
def config_array_to_hash(key)
|
181
221
|
files = {}
|
182
222
|
@config[key].each do |k, v|
|
@@ -185,20 +225,13 @@ module Planter
|
|
185
225
|
@config[key] = files
|
186
226
|
end
|
187
227
|
|
188
|
-
##
|
189
|
-
## Patterns reader, file handling config
|
190
|
-
##
|
191
|
-
## @return [Hash] hash of file patterns
|
192
|
-
##
|
193
|
-
def patterns
|
194
|
-
@patterns ||= process_patterns
|
195
|
-
end
|
196
|
-
|
197
228
|
##
|
198
229
|
## Process :files in config into regex pattern/operator pairs
|
199
230
|
##
|
200
231
|
## @return [Hash] { regex => operator } hash
|
201
232
|
##
|
233
|
+
## @api private
|
234
|
+
##
|
202
235
|
def process_patterns
|
203
236
|
patterns = {}
|
204
237
|
@config[:files].each do |file, oper|
|
@@ -208,15 +241,5 @@ module Planter
|
|
208
241
|
end
|
209
242
|
patterns
|
210
243
|
end
|
211
|
-
|
212
|
-
##
|
213
|
-
## Execute a shell command and return a Boolean success response
|
214
|
-
##
|
215
|
-
## @param cmd [String] The shell command
|
216
|
-
##
|
217
|
-
def pass_fail(cmd)
|
218
|
-
_, status = Open3.capture2("#{cmd} &> /dev/null")
|
219
|
-
status.exitstatus.zero?
|
220
|
-
end
|
221
244
|
end
|
222
245
|
end
|
data/spec/config.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ::Array do
|
6
|
+
let(:array) { [1, 'value1', 'value2', { key1: 'value1', 'key2' => 'value2' }, %w[key1 key2]] }
|
7
|
+
|
8
|
+
describe '.stringify_keys' do
|
9
|
+
it 'converts string keys to strings' do
|
10
|
+
result = array.stringify_keys
|
11
|
+
expect(result).to eq([1, 'value1', 'value2', { 'key1' => 'value1', 'key2' => 'value2' }, %w[key1 key2]])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.abbr_choices' do
|
16
|
+
it 'abbreviates the choices' do
|
17
|
+
arr = ['(o)ption 1', '(s)econd option', '(t)hird option']
|
18
|
+
result = arr.abbr_choices
|
19
|
+
expect(result).to match(%r{{xdw}\[{xbw}o{dw}/{xbw}s{dw}/{xbw}t{dw}\]{x}})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'handles a default' do
|
23
|
+
arr = ['(o)ption 1', '(s)econd option', '(t)hird option']
|
24
|
+
result = arr.abbr_choices(default: 'o')
|
25
|
+
expect(result).to match(%r{{xdw}\[{xbc}o{dw}/{xbw}s{dw}/{xbw}t{dw}\]{x}})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Planter::FileEntry do
|
6
|
+
subject do
|
7
|
+
Planter::FileEntry.new(File.expand_path('spec/templates/test/test.rb'), File.expand_path('spec/test_out/test.rb'),
|
8
|
+
:ignore)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
it 'makes a new instance' do
|
13
|
+
expect(subject).to be_a described_class
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#to_s' do
|
18
|
+
it 'returns the name of the file' do
|
19
|
+
expect(subject.to_s).to be_a(String)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#inspect' do
|
24
|
+
it 'returns a string representation of the file' do
|
25
|
+
expect(subject.inspect).to be_a(String)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#ask_operation' do
|
30
|
+
it 'returns :copy' do
|
31
|
+
expect(subject.ask_operation).to eq(:copy)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns :ignore for existing file' do
|
35
|
+
fileentry = Planter::FileEntry.new(File.expand_path('spec/templates/test/test.rb'), File.expand_path('spec/test_out/test2.rb'),
|
36
|
+
:ignore)
|
37
|
+
expect(fileentry.ask_operation).to eq(:ignore)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ::File do
|
6
|
+
describe '.binary?' do
|
7
|
+
it 'detects a non-binary text file' do
|
8
|
+
expect(File.binary?('spec/test_out/test2.rb')).to be(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'detects a binary image' do
|
12
|
+
expect(File.binary?('spec/test_out/image.png')).to be(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'recognizes json as text' do
|
16
|
+
expect(File.binary?('spec/test_out/doing.sublime-project')).to be(false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Planter::FileList do
|
6
|
+
describe '#initialize' do
|
7
|
+
it 'initializes with an empty list' do
|
8
|
+
Planter.base_dir = File.expand_path('spec')
|
9
|
+
Planter.variables = { project: 'Untitled', script: 'Script', title: 'Title' }
|
10
|
+
Planter.config = 'test'
|
11
|
+
filelist = Planter::FileList.new
|
12
|
+
expect(filelist.files).not_to eq([])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ::Hash do
|
6
|
+
let(:hash) { { 'key1' => 'value1', 'key2' => 'value2' } }
|
7
|
+
|
8
|
+
describe '.symbolize_keys' do
|
9
|
+
it 'converts string keys to symbols' do
|
10
|
+
string_hash = { 'key1' => 'value1', 'key2' => 'value2', 'key3' => ['value3'] }
|
11
|
+
result = string_hash.symbolize_keys
|
12
|
+
expect(result).to eq({ key1: 'value1', key2: 'value2', key3: ['value3'] })
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'handles nested hashes' do
|
16
|
+
nested_hash = { 'outer' => { 'inner' => 'value' } }
|
17
|
+
result = nested_hash.symbolize_keys
|
18
|
+
expect(result).to eq({ outer: { inner: 'value' } })
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'handles empty hashes' do
|
22
|
+
result = {}.symbolize_keys
|
23
|
+
expect(result).to eq({})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.stringify_keys' do
|
28
|
+
it 'converts symbol keys to strings' do
|
29
|
+
symbol_hash = { key1: 'value1', key2: 'value2', key3: ['value3'] }
|
30
|
+
result = symbol_hash.stringify_keys
|
31
|
+
expect(result).to eq({ 'key1' => 'value1', 'key2' => 'value2', 'key3' => ['value3'] })
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'handles nested hashes' do
|
35
|
+
nested_hash = { outer: { inner: 'value' } }
|
36
|
+
result = nested_hash.stringify_keys
|
37
|
+
expect(result).to eq({ 'outer' => { 'inner' => 'value' } })
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles empty hashes' do
|
41
|
+
result = {}.stringify_keys
|
42
|
+
expect(result).to eq({})
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '.deep_merge' do
|
47
|
+
it 'merges two hashes deeply' do
|
48
|
+
hash1 = { a: 1, b: { c: 2 }, f: [1, 2] }
|
49
|
+
hash2 = { b: { d: 3 }, e: 4, f: [3, 4] }
|
50
|
+
result = hash1.deep_merge(hash2)
|
51
|
+
expect(result).to eq({ a: 1, b: { c: 2, d: 3 }, e: 4, f: [1, 2, 3, 4] })
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'handles empty hashes' do
|
55
|
+
result = {}.deep_merge({})
|
56
|
+
expect(result).to eq({})
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'does not modify the original hashes' do
|
60
|
+
hash1 = { a: 1, b: { c: 2 }, f: 'test' }
|
61
|
+
hash2 = { b: { d: 3 }, e: 4, f: nil }
|
62
|
+
hash1.deep_merge(hash2)
|
63
|
+
expect(hash1).to eq({ a: 1, b: { c: 2 }, f: 'test' })
|
64
|
+
expect(hash2).to eq({ b: { d: 3 }, e: 4, f: nil })
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.deep_freeze' do
|
69
|
+
it 'freezes all nested hashes' do
|
70
|
+
hash = { a: 1, b: { c: 2 } }.deep_freeze
|
71
|
+
expect(hash).to be_frozen
|
72
|
+
expect(hash[:b]).to be_frozen
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '.deep_thaw' do
|
77
|
+
it 'thaws all nested hashes' do
|
78
|
+
hash = { a: 1, b: { c: 2 } }.deep_freeze.deep_thaw
|
79
|
+
expect(hash).not_to be_frozen
|
80
|
+
expect(hash[:b]).not_to be_frozen
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/spec/planter/plant_spec.rb
CHANGED