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.
@@ -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.split(/[-_ ]/).map(&:capitalize).join('').gsub(/[^a-z0-9]/i, '')
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]/) { |pair| pair.split('').join('_') }
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(/[ _]+(\S)/) { Regexp.last_match(1).upcase }
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
- gsub(/\b(\w)/) { Regexp.last_match(1).upcase }
99
+ split(/\b(\w+)/).map(&:capitalize).join('')
100
100
  end
101
101
 
102
- ##
103
- ## Apply key/value substitutions to a string. Variables are represented as
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
- Planter.variables.each do |k, v|
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})#{mod_rx}%%/
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
- mods = m['mod']&.split(/:/)
145
- mods&.each do |mod|
146
- v = v.apply_mod(mod.normalize_mod)
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
- return self unless Planter.config.key?(:replacements)
251
+ regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config[:replacements] : regexes
164
252
 
165
- Planter.config[:replacements].stringify_keys.each do |pattern, replacement|
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
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Planter
5
5
  # Current Planter version.
6
- VERSION = '0.0.3'
6
+ VERSION = '0.0.4'
7
7
  end
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
- warn "\n{dw}#{string}{x}".x if @debug
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(BASE_DIR) unless File.directory?(BASE_DIR)
112
- base_config = File.join(BASE_DIR, 'config.yml')
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
- unless File.exist?(base_config)
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
- File.open(base_config, 'w') { |f| f.puts(YAML.dump(default_base_config.stringify_keys)) }
122
- Planter.notify("New configuration written to #{config}, edit as needed.", :warn)
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
- @config = YAML.load(IO.read(base_config)).symbolize_keys
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(BASE_DIR, 'templates', @template)
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
- puts "New configuration written to #{config}, please edit."
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,2 @@
1
+ ---
2
+ git_init: false
@@ -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
@@ -4,6 +4,7 @@ require 'spec_helper'
4
4
 
5
5
  describe Planter::Plant do
6
6
  Planter.accept_defaults = true
7
+ Planter.base_dir = File.expand_path('spec')
7
8
  subject(:ruby_gem) { Planter::Plant.new('test', { project: 'Untitled', script: 'Script', title: 'Title' }) }
8
9
 
9
10
  describe '.new' do