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