toggle 0.0.0.alpha → 1.0.0.rc

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.
@@ -0,0 +1,23 @@
1
+ # Copy this file to ./config.yml or run the following command:
2
+ #
3
+ # $ toggle --copy-defaults [PATH]
4
+ #
5
+ # which will copy {config,key}{,.*}.default files in the given PATH to
6
+ # {config,key}{,.*} (removes .default extension)
7
+ #
8
+ # We're using the configuration conventions & setup:
9
+ # https://git.squareup.com/iacono/toggle#configuration-conventions--setup
10
+ #
11
+ # If you've set up your local variables, you should be able to copy and go!
12
+ #
13
+ # Otherwise, run:
14
+ #
15
+ # $ toggle --init-local
16
+ #
17
+ # And follow the instructions
18
+ :development:
19
+ :some: :development_setting
20
+
21
+ :production:
22
+ :some: :production_setting
23
+ # define any other config blocks that you want!
@@ -0,0 +1,15 @@
1
+ # Copy this file to ./key{.exts} or run the following command:
2
+ #
3
+ # $ toggle --copy-defaults [PATH]
4
+ #
5
+ # which will copy {config,key}{.exts}.default files in the given PATH to
6
+ # {config,key}{exts} (removes .default extension)
7
+ #
8
+ # You can toggle this file to use a particular config block you have
9
+ # set up. To view which top level blocks are available for a given key file,
10
+ # run:
11
+ #
12
+ # $ toggle --keys FILENAME
13
+ #
14
+ # where FILENAME is the name of a given toggle config file.
15
+ development # <= change "development" to whatever you'd like
data/lib/toggle.rb CHANGED
@@ -1,5 +1,78 @@
1
- require "toggle/version"
1
+ require 'toggle/version'
2
+ require 'toggle/compiler'
3
+ require 'toggle/parser'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'active_support/core_ext/string'
2
6
 
3
- module Toggle
4
- # Your code goes here...
7
+ class Toggle
8
+ attr_writer :key
9
+ attr_accessor :key_parsers
10
+ attr_accessor :key_filepath
11
+ attr_accessor :config_parsers
12
+ attr_accessor :config_filepath
13
+
14
+ def initialize attributes = {}
15
+ attributes.keys.each do |attribute|
16
+ self.send "#{attribute}=", attributes[attribute]
17
+ end
18
+ end
19
+
20
+ def [] attribute
21
+ config[attribute]
22
+ end
23
+
24
+ def config_filepath= value
25
+ configs_dirty! unless @config_filepath.eql? value
26
+ @config_filepath = value
27
+ end
28
+
29
+ def key
30
+ (@key || key_from_file).try(:to_sym)
31
+ end
32
+
33
+ def config
34
+ configs[key]
35
+ end
36
+
37
+ def configs
38
+ return @configs if defined?(@configs) && configs_clean?
39
+
40
+ @configs = HashWithIndifferentAccess.new(
41
+ Toggle::Compiler.new(
42
+ config_filepath,
43
+ config_parsers
44
+ ).parsed_content
45
+ )
46
+ configs_clean!
47
+ @configs
48
+ end
49
+
50
+ def using temporary_key
51
+ return unless block_given?
52
+
53
+ previous_key, self.key = self.key, temporary_key
54
+ yield self
55
+ self.key = previous_key
56
+ end
57
+
58
+ private
59
+
60
+ def key_from_file
61
+ Toggle::Compiler.new(
62
+ key_filepath,
63
+ key_parsers
64
+ ).parsed_content if key_filepath
65
+ end
66
+
67
+ def configs_clean?
68
+ @configs_clean
69
+ end
70
+
71
+ def configs_dirty!
72
+ @configs_clean = false
73
+ end
74
+
75
+ def configs_clean!
76
+ @configs_clean = true
77
+ end
5
78
  end
@@ -0,0 +1,31 @@
1
+ class Toggle
2
+ class Compiler
3
+ class FileNotFound < RuntimeError; end
4
+
5
+ def initialize file, parsers = nil
6
+ @file = file
7
+ @parsers = parsers ? [*parsers] : file_extensions
8
+ end
9
+
10
+ def parsed_content
11
+ @parsers.reduce(raw_file_content) do |content, parser|
12
+ Toggle::Parser.for(parser).parse content if content
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def file_extensions
19
+ parts = File.basename(@file).split(".")
20
+ parts[1, parts.length - 1]
21
+ end
22
+
23
+ def raw_file_content
24
+ begin
25
+ File.read(@file).chomp
26
+ rescue ::Errno::ENOENT => e
27
+ raise FileNotFound.new e.message
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ class Toggle
2
+ module Parser
3
+ class ParserNotFound < StandardError; end
4
+
5
+ def self.for type
6
+ case type.to_s.downcase
7
+ when 'yaml', 'yml'
8
+ require 'toggle/parser/yaml'
9
+ ::Toggle::Parser::YAML.new
10
+ else
11
+ raise ParserNotFound, <<-EOS
12
+ #{type} is not currently implemented. You should write it!
13
+ EOS
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ # YAML Parser handles erb parsing first, then loads the content to yaml
2
+ class Toggle
3
+ module Parser
4
+ class YAML
5
+ def parse content
6
+ require 'erb'
7
+ require 'yaml'
8
+ ::YAML.load(::ERB.new(content).result.chomp)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
- module Toggle
2
- VERSION = "0.0.0.alpha"
1
+ class Toggle
2
+ VERSION = "1.0.0.rc"
3
3
  end
data/script/ci ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundler_version="1.1.4"
4
+
5
+ source "$HOME/.rvm/scripts/rvm"
6
+
7
+ function install_ruby_if_needed() {
8
+ echo "Checking for $1..."
9
+ if ! rvm list rubies | grep $1 > /dev/null; then
10
+ echo "Ruby not found $1..."
11
+ rvm install $1
12
+ fi
13
+ }
14
+
15
+ function switch_ruby() {
16
+ install_ruby_if_needed $1 && rvm use $1
17
+ }
18
+
19
+ function install_bundler_if_needed() {
20
+ echo "Checking for Bundler $bundler_version..."
21
+ if ! gem list --installed bundler --version "$bundler_version" > /dev/null; then
22
+ gem install bundler --version "$bundler_version"
23
+ fi
24
+ }
25
+
26
+ function update_gems_if_needed() {
27
+ echo "Installing gems..."
28
+ bundle check || bundle install
29
+ }
30
+
31
+ function run_tests() {
32
+ bundle exec rake
33
+ }
34
+
35
+ function prepare_and_run() {
36
+ switch_ruby $1 &&
37
+ install_bundler_if_needed &&
38
+ update_gems_if_needed &&
39
+ run_tests
40
+ }
41
+
42
+ prepare_and_run "1.9.3-p194"
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,427 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ def clear_toggle_files_from path
5
+ Dir.glob(File.join(path, '*'), File::FNM_DOTMATCH).each do |file|
6
+ FileUtils.rm_f file if file =~ /((config|key)(\..*)?$|\.toggle\.local)/
7
+ end
8
+ end
9
+
10
+ describe "CLI" do
11
+ let(:test_dir) { File.join(File.expand_path('..', __FILE__), 'test', 'fixtures', 'cli') }
12
+
13
+ describe "--ensure-key" do
14
+ let(:default_key_file) { File.join(test_dir, 'key.yml.default') }
15
+ let(:key_file) { File.join(test_dir, 'key.yml') }
16
+ let(:content) { 'some_key' }
17
+
18
+ before :all do
19
+ clear_toggle_files_from test_dir
20
+ end
21
+
22
+ before :each do
23
+ %x(echo "#{content}" > #{default_key_file})
24
+ end
25
+
26
+ after :each do
27
+ clear_toggle_files_from test_dir
28
+ end
29
+
30
+ describe "when key does not exist" do
31
+ it "copies the default key to the actual" do
32
+ %x(./bin/toggle --ensure-key #{test_dir})
33
+ FileUtils.identical?(key_file, default_key_file).should == true
34
+ end
35
+ end
36
+
37
+ describe "when key already exists" do
38
+ let(:key_content) { 'existing_key' }
39
+
40
+ before do
41
+ %x(echo "#{key_content}" > #{key_file})
42
+ end
43
+
44
+ it "leaves the current key alone" do
45
+ %x(./bin/toggle --ensure-key #{test_dir})
46
+ File.read(key_file).chomp.should == key_content
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "--copy-config-defaults" do
52
+ let(:default_config_file) { File.join(test_dir, 'config.yml.default') }
53
+ let(:actual_config_file) { File.join(test_dir, 'config.yml') }
54
+ let(:default_config_content) { 'DEFAULT CONFIG CONTENT' }
55
+
56
+ before :all do
57
+ clear_toggle_files_from test_dir
58
+ end
59
+
60
+ before :each do
61
+ %x(echo "#{default_config_content}" > #{default_config_file})
62
+ end
63
+
64
+ after :each do
65
+ clear_toggle_files_from test_dir
66
+ end
67
+
68
+ it "does not copy the key default" do
69
+ default_key_file = File.join(test_dir, 'key.yml.default')
70
+ actual_key_file = File.join(test_dir, 'key.yml')
71
+ default_key_content = 'some_key'
72
+
73
+ %x(echo "#{default_key_content}" > #{default_key_file})
74
+ %x(./bin/toggle --copy-config-defaults #{test_dir})
75
+ File.exists?(actual_key_file).should == false
76
+ end
77
+
78
+ describe "no config exists" do
79
+ it "copies the default config to the actual" do
80
+ %x(./bin/toggle --copy-config-defaults #{test_dir})
81
+ FileUtils.identical?(actual_config_file, default_config_file).should == true
82
+ end
83
+ end
84
+
85
+ describe "config is identical to default" do
86
+ before do
87
+ %x(cp #{default_config_file} #{actual_config_file})
88
+ end
89
+
90
+ it "leaves current config unchanged" do
91
+ %x(./bin/toggle --copy-config-defaults #{test_dir})
92
+ FileUtils.identical?(actual_config_file, default_config_file).should == true
93
+ end
94
+ end
95
+
96
+ describe "actual is present and different from default" do
97
+ let(:different_content) { "#{default_config_content} BUT DIFFERENT" }
98
+
99
+ before do
100
+ %x(echo "#{different_content}" > #{actual_config_file})
101
+ end
102
+
103
+ it "leaves current config unchanged when user responds with anything but 'y' words" do
104
+ %x(echo 'n' | ./bin/toggle --copy-config-defaults #{test_dir})
105
+ FileUtils.identical?(actual_config_file, default_config_file).should == false
106
+ end
107
+
108
+ it "replaces current config with default when user responds with 'y' words" do
109
+ %x(echo 'y' | ./bin/toggle --copy-config-defaults #{test_dir})
110
+ FileUtils.identical?(actual_config_file, default_config_file).should == true
111
+ File.read(actual_config_file).chomp.should == default_config_content
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "--copy-defaults" do
117
+ let(:config_default_file) { File.join(test_dir, 'config.yml.default') }
118
+ let(:config_actual_file) { File.join(test_dir, 'config.yml') }
119
+ let(:config_default_content) { 'DEFAULT CONTENT' }
120
+ let(:key_default_file) { File.join(test_dir, 'key.yml.default') }
121
+ let(:key_actual_file) { File.join(test_dir, 'key.yml') }
122
+ let(:key_default_content) { 'DEFAULT CONTENT' }
123
+
124
+ before :all do
125
+ clear_toggle_files_from test_dir
126
+ end
127
+
128
+ before :each do
129
+ %x(echo "#{config_default_content}" > #{config_default_file})
130
+ %x(echo "#{key_default_content}" > #{key_default_file})
131
+ end
132
+
133
+ after :each do
134
+ clear_toggle_files_from test_dir
135
+ end
136
+
137
+ describe "copies default config and key file" do
138
+ it "copies each default file over to its appropriate location" do
139
+ %x(./bin/toggle --copy-defaults #{test_dir})
140
+ FileUtils.identical?(config_default_file, config_actual_file).should == true
141
+ FileUtils.identical?(key_default_file, key_actual_file).should == true
142
+ end
143
+ end
144
+
145
+ describe "actual is identical to default" do
146
+ before do
147
+ %x(cp #{config_default_file} #{config_actual_file})
148
+ end
149
+
150
+ it "leaves current file unchanged" do
151
+ %x(./bin/toggle --copy-defaults #{test_dir})
152
+ FileUtils.identical?(config_default_file, config_actual_file).should == true
153
+ end
154
+ end
155
+
156
+ describe "actual is present but has different content from default" do
157
+ let(:different_content) { "#{config_default_content} BUT DIFFERENT" }
158
+
159
+ before do
160
+ %x(echo "#{different_content}" > #{config_actual_file})
161
+ end
162
+
163
+ it "leaves current file unchanged when user responds with anything but 'y' words" do
164
+ %x(echo 'n' | ./bin/toggle --copy-defaults #{test_dir})
165
+ FileUtils.identical?(config_default_file, config_actual_file).should == false
166
+ File.read(config_actual_file).chomp.should == different_content
167
+ end
168
+
169
+ it "replaces current file with default when user responds with 'y' words" do
170
+ %x(echo 'y' | ./bin/toggle --copy-defaults #{test_dir})
171
+ FileUtils.identical?(config_default_file, config_actual_file).should == true
172
+ File.read(config_actual_file).chomp.should == config_default_content
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "--keys" do
178
+ let(:config_file) { File.join(FIXTURES_PATH, 'config.yml') }
179
+
180
+ it "alerts the user if the file is not found" do
181
+ %x(./bin/toggle --keys /path/to/nothing).chomp.should == "toggle config file not found, please check specified path"
182
+ end
183
+
184
+ it "can be queried for the available keys from the commandline" do
185
+ %x(./bin/toggle --keys #{config_file}).chomp.should == "- local\n- remote"
186
+ end
187
+ end
188
+
189
+ describe "--values" do
190
+ let(:config_file) { File.join(FIXTURES_PATH, 'config.yml') }
191
+
192
+ it "alerts the user if the file is not found" do
193
+ %x(./bin/toggle --values /path/to/nothing).chomp.should == "toggle config file not found, please check specified path"
194
+ end
195
+
196
+ it "can be queried for the available keys from the commandline" do
197
+ %x(./bin/toggle --values #{config_file},local).should == <<-EOS.strip_heredoc
198
+ ---
199
+ :plain_attribute: local_plain_attribute_value
200
+ :erb_attribute: local_erb_attribute_value
201
+ EOS
202
+ end
203
+ end
204
+
205
+ describe "--init-local" do
206
+ let(:file) { File.join(test_dir, '.toggle.local') }
207
+
208
+ before :all do
209
+ clear_toggle_files_from test_dir
210
+ end
211
+
212
+ after :each do
213
+ clear_toggle_files_from test_dir
214
+ end
215
+
216
+ describe "file does not exist" do
217
+ it "adds .toggle.local with commons var placeholders" do
218
+ %x(./bin/toggle --init-local #{test_dir})
219
+ File.read(file).chomp.should == <<-EOS.strip_heredoc
220
+ # Add any variables that you'd like below.
221
+ #
222
+ # We've included a few suggestions, but please feel free
223
+ # to modify as needed.
224
+ #
225
+ # Make sure that you source this file in your ~/.bash_profile
226
+ # or ~/.bashrc (or whereever you'd like) via:
227
+ #
228
+ # if [ -s ~/.toggle.local ] ; then source ~/.toggle.local ; fi
229
+ export DATABASE_HOST=''
230
+ export DATABASE_NAME=''
231
+ export DATABASE_USERNAME=''
232
+ export DATABASE_PASSWORD=''
233
+ export USER_EMAIL=''
234
+ EOS
235
+ end
236
+ end
237
+
238
+ describe "file already exists" do
239
+ before do
240
+ %x(echo "SOME CONTENT" > #{file})
241
+ end
242
+
243
+ it "leaves .toggle.local unchanged when user responds with anything but 'y' words" do
244
+ %x(echo 'n' | ./bin/toggle --init-local #{test_dir})
245
+ File.read(file).chomp.should == 'SOME CONTENT'
246
+ end
247
+
248
+ it "replaces .toggle.local with default when user responds with 'y' words" do
249
+ %x(echo 'y' | ./bin/toggle --init-local #{test_dir})
250
+ File.read(file).chomp.should == <<-EOS.strip_heredoc
251
+ # Add any variables that you'd like below.
252
+ #
253
+ # We've included a few suggestions, but please feel free
254
+ # to modify as needed.
255
+ #
256
+ # Make sure that you source this file in your ~/.bash_profile
257
+ # or ~/.bashrc (or whereever you'd like) via:
258
+ #
259
+ # if [ -s ~/.toggle.local ] ; then source ~/.toggle.local ; fi
260
+ export DATABASE_HOST=''
261
+ export DATABASE_NAME=''
262
+ export DATABASE_USERNAME=''
263
+ export DATABASE_PASSWORD=''
264
+ export USER_EMAIL=''
265
+ EOS
266
+ end
267
+ end
268
+ end
269
+
270
+ describe "--make-defaults" do
271
+ let(:actual_key_file) { File.join(test_dir, 'key.yml') }
272
+ let(:default_key_file) { File.join(test_dir, 'key.yml.default') }
273
+ let(:actual_config_file) { File.join(test_dir, 'config.yml') }
274
+ let(:default_config_file) { File.join(test_dir, 'config.yml.default') }
275
+
276
+ before :all do
277
+ clear_toggle_files_from test_dir
278
+ end
279
+
280
+ after :each do
281
+ clear_toggle_files_from test_dir
282
+ end
283
+
284
+ describe "when user has not created any actual or .default files" do
285
+ it "creates a default key + config file in the passed path" do
286
+ %x(./bin/toggle --make-defaults #{test_dir})
287
+ File.read(default_key_file).should == <<-EOS.strip_heredoc
288
+ # Copy this file to ./key{.exts} or run the following command:
289
+ #
290
+ # $ toggle --copy-defaults [PATH]
291
+ #
292
+ # which will copy {config,key}{.exts}.default files in the given PATH to
293
+ # {config,key}{exts} (removes .default extension)
294
+ #
295
+ # You can toggle this file to use a particular config block you have
296
+ # set up. To view which top level blocks are available for a given key file,
297
+ # run:
298
+ #
299
+ # $ toggle --keys FILENAME
300
+ #
301
+ # where FILENAME is the name of a given toggle config file.
302
+ development # <= change "development" to whatever you'd like
303
+ EOS
304
+
305
+ File.read(default_config_file).should == <<-EOS.strip_heredoc
306
+ # Copy this file to ./config.yml or run the following command:
307
+ #
308
+ # $ toggle --copy-defaults [PATH]
309
+ #
310
+ # which will copy {config,key}{,.*}.default files in the given PATH to
311
+ # {config,key}{,.*} (removes .default extension)
312
+ #
313
+ # We're using the configuration conventions & setup:
314
+ # https://git.squareup.com/iacono/toggle#configuration-conventions--setup
315
+ #
316
+ # If you've set up your local variables, you should be able to copy and go!
317
+ #
318
+ # Otherwise, run:
319
+ #
320
+ # $ toggle --init-local
321
+ #
322
+ # And follow the instructions
323
+ :development:
324
+ :some: :development_setting
325
+
326
+ :production:
327
+ :some: :production_setting
328
+ # define any other config blocks that you want!
329
+ EOS
330
+ end
331
+ end
332
+
333
+ describe "when user has created an actual key file but the corresponding .default file does not exist" do
334
+ before do
335
+ %x(echo "SOME CONTENT" > #{actual_key_file})
336
+ end
337
+
338
+ it "copies the actual user created key file to the corresponding .default" do
339
+ %x(./bin/toggle --make-defaults #{test_dir})
340
+ File.read(default_key_file).should == "SOME CONTENT\n"
341
+ end
342
+ end
343
+
344
+ describe "when user has created an actual config file but the corresponding .default file does not exist" do
345
+ before do
346
+ %x(echo "SOME CONTENT" > #{actual_config_file})
347
+ end
348
+
349
+ it "copies the actual user created file to the corresponding .default" do
350
+ %x(./bin/toggle --make-defaults #{test_dir})
351
+ File.read(default_config_file).should == "SOME CONTENT\n"
352
+ end
353
+ end
354
+
355
+ describe "when user has created .default key file" do
356
+ before do
357
+ %x(echo "SOME CONTENT" > #{default_key_file})
358
+ end
359
+
360
+ it "leaves the .default file unchanged when user responds with anything but 'y' words" do
361
+ %x(echo 'n' | ./bin/toggle --make-defaults #{test_dir})
362
+ File.read(default_key_file).should == "SOME CONTENT\n"
363
+ end
364
+
365
+ it "replaces the .default file with default when user responds with 'y' words" do
366
+ %x(echo 'y' | ./bin/toggle --make-defaults #{test_dir})
367
+ File.read(default_key_file).should == <<-EOS.strip_heredoc
368
+ # Copy this file to ./key{.exts} or run the following command:
369
+ #
370
+ # $ toggle --copy-defaults [PATH]
371
+ #
372
+ # which will copy {config,key}{.exts}.default files in the given PATH to
373
+ # {config,key}{exts} (removes .default extension)
374
+ #
375
+ # You can toggle this file to use a particular config block you have
376
+ # set up. To view which top level blocks are available for a given key file,
377
+ # run:
378
+ #
379
+ # $ toggle --keys FILENAME
380
+ #
381
+ # where FILENAME is the name of a given toggle config file.
382
+ development # <= change "development" to whatever you'd like
383
+ EOS
384
+ end
385
+ end
386
+
387
+ describe "when user has created .default config file" do
388
+ before do
389
+ %x(echo "SOME CONTENT" > #{default_config_file})
390
+ end
391
+
392
+ it "leaves current config file unchanged when user responds with anything but 'y' words" do
393
+ %x(echo 'n' | ./bin/toggle --make-defaults #{test_dir})
394
+ File.read(default_config_file).should == "SOME CONTENT\n"
395
+ end
396
+
397
+ it "replaces current config file with default when user responds with 'y' words" do
398
+ %x(echo 'y' | ./bin/toggle --make-defaults #{test_dir})
399
+ File.read(default_config_file).should == <<-EOS.strip_heredoc
400
+ # Copy this file to ./config.yml or run the following command:
401
+ #
402
+ # $ toggle --copy-defaults [PATH]
403
+ #
404
+ # which will copy {config,key}{,.*}.default files in the given PATH to
405
+ # {config,key}{,.*} (removes .default extension)
406
+ #
407
+ # We're using the configuration conventions & setup:
408
+ # https://git.squareup.com/iacono/toggle#configuration-conventions--setup
409
+ #
410
+ # If you've set up your local variables, you should be able to copy and go!
411
+ #
412
+ # Otherwise, run:
413
+ #
414
+ # $ toggle --init-local
415
+ #
416
+ # And follow the instructions
417
+ :development:
418
+ :some: :development_setting
419
+
420
+ :production:
421
+ :some: :production_setting
422
+ # define any other config blocks that you want!
423
+ EOS
424
+ end
425
+ end
426
+ end
427
+ end