cliutils 2.1.4 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +3 -2
  4. data/Gemfile +7 -0
  5. data/HISTORY.md +7 -0
  6. data/README.md +3 -1
  7. data/Rakefile +8 -1
  8. data/cliutils.gemspec +1 -0
  9. data/lib/cliutils/configuration.rb +1 -1
  10. data/lib/cliutils/constants.rb +1 -1
  11. data/lib/cliutils/ext/hash_extensions.rb +16 -12
  12. data/lib/cliutils/messaging.rb +2 -116
  13. data/lib/cliutils/messenger.rb +109 -0
  14. data/lib/cliutils/prefs/pref.rb +10 -10
  15. data/lib/cliutils/prefs/pref_actions/open_url_action.rb +1 -1
  16. data/lib/cliutils/prefs/pref_validators/filepath_exists_validator.rb +2 -0
  17. data/lib/cliutils/prefs/pref_validators/url_validator.rb +1 -1
  18. data/lib/cliutils/prefs.rb +1 -1
  19. data/lib/cliutils/pretty_io.rb +0 -10
  20. data/lib/cliutils.rb +1 -1
  21. data/spec/action/open_url_action_spec.rb +20 -0
  22. data/spec/action/pref_action_spec.rb +11 -0
  23. data/spec/behavior/capitalize_behavior_spec.rb +11 -0
  24. data/spec/behavior/expand_filepath_behavior_spec.rb +11 -0
  25. data/spec/behavior/lowercase_behavior_spec.rb +11 -0
  26. data/spec/behavior/pref_behavior_spec.rb +11 -0
  27. data/spec/behavior/prefix_behavior_spec.rb +12 -0
  28. data/spec/behavior/suffix_behavior_spec.rb +12 -0
  29. data/spec/behavior/titlecase_behavior_spec.rb +10 -0
  30. data/spec/behavior/uppercase_behavior_spec.rb +11 -0
  31. data/spec/configuration_spec.rb +37 -0
  32. data/spec/configurator_spec.rb +104 -0
  33. data/spec/ext/hash_extensions_spec.rb +54 -0
  34. data/spec/ext/logger_extensions_spec.rb +20 -0
  35. data/spec/ext/string_extensions_spec.rb +26 -0
  36. data/spec/messaging_spec.rb +91 -0
  37. data/spec/pref_spec.rb +144 -0
  38. data/spec/prefs_spec.rb +150 -0
  39. data/spec/spec_helper.rb +16 -0
  40. data/spec/validator/alphabetic_validator_spec.rb +20 -0
  41. data/spec/validator/alphanumeric_validator_spec.rb +20 -0
  42. data/spec/validator/date_validator_spec.rb +20 -0
  43. data/spec/validator/datetime_validator_spec.rb +20 -0
  44. data/spec/validator/filepath_exists_validator_spec.rb +20 -0
  45. data/spec/validator/non_nil_validator_spec.rb +24 -0
  46. data/spec/validator/number_validator_spec.rb +20 -0
  47. data/spec/validator/pref_validator_spec.rb +11 -0
  48. data/spec/validator/time_validator_spec.rb +20 -0
  49. data/spec/validator/url_validator_spec.rb +20 -0
  50. data/{test/test_files → support}/configuration.yaml +1 -1
  51. data/support/prefstest.yaml +27 -0
  52. data/{test/test_files → support}/test_action.rb +0 -0
  53. data/support/test_action_empty.rb +7 -0
  54. data/{test/test_files → support}/test_behavior.rb +2 -2
  55. data/support/test_behavior_empty.rb +6 -0
  56. data/{test/test_files → support}/test_validator.rb +0 -2
  57. data/support/test_validator_empty.rb +7 -0
  58. data/test/pref_test.rb +0 -1
  59. data/test/prefs_test.rb +64 -1
  60. data/test/test_helper.rb +2 -2
  61. metadata +91 -67
  62. data/lib/cliutils/logger_delegator.rb +0 -49
  63. data/test/action_tests/open_url_action_test.rb +0 -12
  64. data/test/behavior_tests/capitalize_behavior_test.rb +0 -11
  65. data/test/behavior_tests/expand_filepath_behavior_test.rb +0 -11
  66. data/test/behavior_tests/lowercase_behavior_test.rb +0 -11
  67. data/test/behavior_tests/prefix_behavior_test.rb +0 -12
  68. data/test/behavior_tests/suffix_behavior_test.rb +0 -12
  69. data/test/behavior_tests/titlecase_behavior_test.rb +0 -11
  70. data/test/behavior_tests/uppercase_behavior_test.rb +0 -11
  71. data/test/configuration_test.rb +0 -49
  72. data/test/configurator_test.rb +0 -63
  73. data/test/hash_extensions_test.rb +0 -51
  74. data/test/logger_extensions_test.rb +0 -17
  75. data/test/messaging_test.rb +0 -53
  76. data/test/string_extesions_test.rb +0 -28
  77. data/test/test_files/prefstest.yaml +0 -38
  78. data/test/validator_tests/alphabetic_validator_test.rb +0 -22
  79. data/test/validator_tests/alphanumeric_validator_test.rb +0 -22
  80. data/test/validator_tests/date_validator_test.rb +0 -22
  81. data/test/validator_tests/datetime_validator_test.rb +0 -22
  82. data/test/validator_tests/filepath_exists_validator_test.rb +0 -22
  83. data/test/validator_tests/non_nil_validator_test.rb +0 -30
  84. data/test/validator_tests/number_validator_test.rb +0 -22
  85. data/test/validator_tests/time_validator_test.rb +0 -22
  86. data/test/validator_tests/url_validator_test.rb +0 -22
@@ -9,7 +9,7 @@ module CLIUtils
9
9
  def run
10
10
  url = @parameters[:url]
11
11
  Launchy.open(url) do |exception|
12
- puts "Failed to open #{ url }: #{ exception }"
12
+ fail "Failed to open URL: #{ exception }" if exception
13
13
  end
14
14
  end
15
15
  end
@@ -1,3 +1,5 @@
1
+ require 'pathname'
2
+
1
3
  module CLIUtils
2
4
  # A Validator to verify whether a Pref answer
3
5
  # is a local filepath that exists.
@@ -9,7 +9,7 @@ module CLIUtils
9
9
  # @return [String]
10
10
  def validate(text)
11
11
  @is_valid = text.to_s =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]
12
- @message = "Response is not a url: #{ text }"
12
+ @message = "Response is not a URL: #{ text }"
13
13
  end
14
14
  end
15
15
  end
@@ -54,7 +54,7 @@ module CLIUtils
54
54
  data = YAML.load_file(data).deep_symbolize_keys
55
55
  @prompts = _generate_prefs(data)
56
56
  else
57
- fail "Invalid configuration file: #{ data }"
57
+ fail "Invalid configuration file: #{ data }"
58
58
  end
59
59
  when Hash
60
60
  @config_path = nil
@@ -1,13 +1,3 @@
1
- begin
2
- unless (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM).nil?
3
- require 'Win32/Console/ANSI'
4
- end
5
- rescue LoadError
6
- raise 'You must run `gem install win32console` to use CLIMessage on Windows'
7
- end
8
-
9
- require 'readline'
10
-
11
1
  module CLIUtils
12
2
  # CLIMessenger Module
13
3
  # Outputs color-coordinated messages to a CLI
data/lib/cliutils.rb CHANGED
@@ -6,7 +6,7 @@ require 'cliutils/constants'
6
6
  require 'cliutils/pretty_io'
7
7
  require 'cliutils/configurator'
8
8
  require 'cliutils/configuration'
9
- require 'cliutils/logger_delegator'
9
+ require 'cliutils/messenger'
10
10
  require 'cliutils/messaging'
11
11
  require 'cliutils/prefs'
12
12
  require 'cliutils/prefs/pref'
@@ -0,0 +1,20 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_actions/pref_action'
4
+ require 'cliutils/prefs/pref_actions/open_url_action'
5
+
6
+ describe CLIUtils::OpenUrlAction do
7
+ it 'opens a website with the specified parameter' do
8
+ a = CLIUtils::OpenUrlAction.new
9
+ a.parameters = { url: 'http://www.google.com' }
10
+ expect(Launchy).to receive(:open).with('http://www.google.com')
11
+ a.run
12
+ end
13
+
14
+ it 'throws an exception with a bad URL' do
15
+ a = CLIUtils::OpenUrlAction.new
16
+ a.parameters = { url: 'bachya' }
17
+ m = "Failed to open URL: No application found to handle 'bachya'"
18
+ expect { a.run }.to raise_error(RuntimeError, m)
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_actions/pref_action'
4
+ require File.join(File.dirname(__FILE__), '..', '..', 'support/test_action_empty')
5
+
6
+ describe CLIUtils::TestActionEmpty do
7
+ it 'raises an exception if `run` is not implemented' do
8
+ m = '`run` method not implemented on caller: CLIUtils::TestActionEmpty'
9
+ expect { CLIUtils::TestActionEmpty.new.run }.to raise_error(RuntimeError, m)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/capitalize_behavior'
5
+
6
+ describe CLIUtils::CapitalizeBehavior do
7
+ it 'capitalizes its input' do
8
+ b = CLIUtils::CapitalizeBehavior.new
9
+ expect(b.evaluate('bachya')).to eq('Bachya')
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/expand_filepath_behavior'
5
+
6
+ describe CLIUtils::ExpandFilepathBehavior do
7
+ it 'runs File.expand_path on its input' do
8
+ b = CLIUtils::ExpandFilepathBehavior.new
9
+ expect(b.evaluate('~/test')).to eq("#{ ENV['HOME'] }/test")
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/lowercase_behavior'
5
+
6
+ describe CLIUtils::LowercaseBehavior do
7
+ it 'lowercases its input' do
8
+ b = CLIUtils::LowercaseBehavior.new
9
+ expect(b.evaluate('BaChYa')).to eq('bachya')
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require File.join(File.dirname(__FILE__), '..', '..', 'support/test_behavior_empty')
5
+
6
+ describe CLIUtils::TestBehaviorEmpty do
7
+ it 'raises an exception if `evaluate` is not implemented' do
8
+ m = '`evaluate` method not implemented on caller: CLIUtils::TestBehaviorEmpty'
9
+ expect { CLIUtils::TestBehaviorEmpty.new.evaluate('') }.to raise_error(RuntimeError, m)
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/prefix_behavior'
5
+
6
+ describe CLIUtils::PrefixBehavior do
7
+ it 'prefixes its input' do
8
+ b = CLIUtils::PrefixBehavior.new
9
+ b.parameters = { prefix: 'Starting up: ' }
10
+ expect(b.evaluate('bachya')).to eq('Starting up: bachya')
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/suffix_behavior'
5
+
6
+ describe CLIUtils::SuffixBehavior do
7
+ it 'prefixes its input' do
8
+ b = CLIUtils::SuffixBehavior.new
9
+ b.parameters = { suffix: ' - signing off!' }
10
+ expect(b.evaluate('bachya')).to eq('bachya - signing off!')
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ require 'cliutils/messaging'
2
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
3
+ require 'cliutils/prefs/pref_behaviors/titlecase_behavior'
4
+
5
+ describe CLIUtils::TitlecaseBehavior do
6
+ it 'titlecases its input' do
7
+ b = CLIUtils::TitlecaseBehavior.new
8
+ expect(b.evaluate('my sentence is here')).to eq('My Sentence Is Here')
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/prefs/pref_behaviors/pref_behavior'
4
+ require 'cliutils/prefs/pref_behaviors/uppercase_behavior'
5
+
6
+ describe CLIUtils::UppercaseBehavior do
7
+ it 'uppercases its input' do
8
+ b = CLIUtils::UppercaseBehavior.new
9
+ expect(b.evaluate('bachya')).to eq('BACHYA')
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/configuration'
4
+ require 'cliutils/configurator'
5
+
6
+ describe CLIUtils::Configuration do
7
+ include CLIUtils::Configuration
8
+
9
+ it 'raises an exception if not loaded properly' do
10
+ m = 'Attempted to access `configuration` before executing `load_configuration`'
11
+ expect { configuration }.to raise_error(RuntimeError, m)
12
+ end
13
+
14
+ let(:config_path_new) { File.expand_path('support/configuration2.yaml') }
15
+ it 'initializies configuration from scratch' do
16
+ load_configuration(config_path_new)
17
+ expect(configuration.class).to eq(CLIUtils::Configurator)
18
+ expect(configuration.config_path).to eq(config_path_new)
19
+ expect(configuration.data).to eq({})
20
+ end
21
+
22
+ let(:config_path_existing) { File.expand_path('support/configuration.yaml') }
23
+ let(:existing_data) { { my_app: {
24
+ config_location: '/Users/bob/.my-app-config',
25
+ log_level: 'WARN',
26
+ version: '1.0.0' },
27
+ user_data: {
28
+ username: 'bob',
29
+ age: 45 } } }
30
+
31
+ it 'works with existing configuration data' do
32
+ load_configuration(config_path_existing)
33
+ expect(configuration.class).to eq(CLIUtils::Configurator)
34
+ expect(configuration.config_path).to eq(config_path_existing)
35
+ expect(configuration.data).to eq(existing_data)
36
+ end
37
+ end
@@ -0,0 +1,104 @@
1
+ require_relative 'spec_helper'
2
+ require 'cliutils/messaging'
3
+ require 'cliutils/configurator'
4
+ require 'cliutils/ext/hash_extensions'
5
+ require 'cliutils/prefs'
6
+ require 'yaml'
7
+
8
+ describe CLIUtils::Configuration do
9
+ include CLIUtils::Configuration
10
+
11
+ let(:config_path) { File.expand_path('support/configuration.yaml') }
12
+ let(:prefs_path) { File.expand_path('support/prefstest.yaml') }
13
+ let(:config) { CLIUtils::Configurator.new(config_path)}
14
+ let(:existing_data) { { my_app: {
15
+ config_location: '/Users/bob/.my-app-config',
16
+ log_level: 'WARN',
17
+ version: '1.0.0' },
18
+ user_data: {
19
+ username: 'bob',
20
+ age: 45 } } }
21
+
22
+ it 'adds a section to the config data' do
23
+ config.add_section(:test)
24
+ expect(config.data).to eq(existing_data.merge!(test: {}))
25
+ end
26
+
27
+ it 'raises an exception when trying to add an already-existing section' do
28
+ config.add_section(:test)
29
+ m = 'Section already exists: test'
30
+ expect { config.add_section(:test) }.to raise_error(RuntimeError, m)
31
+ end
32
+
33
+ it 'backs up the configuration file' do
34
+ backup_path = config.backup
35
+ expect(backup_path).to eq("#{ config_path }-#{ Time.now.to_i }")
36
+ FileUtils.rm(backup_path)
37
+ end
38
+
39
+ it 'deletes a section from the config data' do
40
+ config.add_section(:test)
41
+ config.add_section(:test2)
42
+ config.delete_section(:test)
43
+ expect(config.data).to eq(existing_data.merge!(test2: {}))
44
+ end
45
+
46
+ it 'raises an exception when trying to delete a nonexistent section' do
47
+ m = 'Cannot delete nonexistent section: test'
48
+ expect { config.delete_section(:test) }.to raise_error(RuntimeError, m)
49
+ end
50
+
51
+ it 'allows data retrieval via a hash or magic methods' do
52
+ config.add_section(:test)
53
+ config.data[:test].merge!(name: 'Bob')
54
+ expect(config.data[:test][:name]).to eq('Bob')
55
+ expect(config.test[:name]).to eq('Bob')
56
+ end
57
+
58
+ it 'resets the entire data collection' do
59
+ config.add_section(:test)
60
+ config.data[:test].merge!(name: 'Bob')
61
+ config.reset
62
+ expect(config.data).to eq({})
63
+ end
64
+
65
+ it 'saves its data to a file' do
66
+ config.add_section(:section1)
67
+ config.section1.merge!(a: 'test', b: 'test')
68
+ config.save
69
+
70
+ saved_data = YAML.load_file(config.config_path).deep_symbolize_keys
71
+ expect(saved_data).to eq(config.data)
72
+
73
+ config.delete_section(:section1)
74
+ config.save
75
+ end
76
+
77
+ it 'compares versions' do
78
+ config.add_section(:app_data)
79
+ config.app_data.merge!(VERSION: '1.0.0')
80
+ config.current_version = config.app_data[:VERSION]
81
+ config.last_version = '1.0.8'
82
+ config.compare_version do |c, l|
83
+ expect(c).to be < l
84
+ end
85
+ end
86
+
87
+ it 'successfully ingests Prefs into its data' do
88
+ config.ingest_prefs(CLIUtils::Prefs.new(prefs_path))
89
+ h = {
90
+ personal_info: {
91
+ superhero: nil,
92
+ batman_answer: nil,
93
+ superman_answer: nil,
94
+ no_clue: nil
95
+ }
96
+ }
97
+ expect(existing_data.merge!(h)).to eq(config.data)
98
+ end
99
+
100
+ it 'raises an exception when attempting to ingest invalid Prefs' do
101
+ m = 'Invaid Prefs class'
102
+ expect { config.ingest_prefs(123) }.to raise_error(RuntimeError, m)
103
+ end
104
+ end
@@ -0,0 +1,54 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/ext/hash_extensions'
3
+
4
+ describe Hash do
5
+ it 'allows for deep merging' do
6
+ h1 = { key: 'value', key2: %w(value1, value2) }
7
+ h2 = { key: 'another_value', key3: { subkey1: 'value' } }
8
+ exp_result = { key: 'another_value', key2: ['value1,', 'value2'], key3: { subkey1: 'value' } }
9
+ actual_result = h1.deep_merge!(h2)
10
+
11
+ expect(actual_result).to eq(exp_result)
12
+ end
13
+
14
+ it 'allows for a new hash to be created with stringified keys' do
15
+ h = { key: { subkey1: 'value1', subkey2: { subsubkey1: 'value' } } }
16
+ exp_result = { 'key' => { 'subkey1' => 'value1', 'subkey2' => { 'subsubkey1' => 'value' } } }
17
+ actual_result = h.deep_stringify_keys
18
+
19
+ expect(actual_result).to_not eq(h)
20
+ expect(actual_result).to eq(exp_result)
21
+ end
22
+
23
+ it 'allows for inline key stringification' do
24
+ h = {key: {subkey1: 'value1', subkey2: {subsubkey1: 'value'}}}
25
+ exp_result = { 'key' => { 'subkey1' => 'value1', 'subkey2' => { 'subsubkey1' => 'value' } } }
26
+ actual_result = h.deep_stringify_keys!
27
+
28
+ expect(actual_result).to eq(h)
29
+ expect(actual_result).to eq(exp_result)
30
+ end
31
+
32
+ it 'allows for a new hash to be created with symbolized keys' do
33
+ h = { 'key' => { 'subkey1' => 'value1', 'subkey2' => { 'subsubkey1' => ['value1', 'value2']} } }
34
+ exp_result = { key: { subkey1: 'value1', subkey2: { subsubkey1: ['value1', 'value2']} } }
35
+ actual_result = h.deep_symbolize_keys
36
+
37
+ expect(actual_result).to_not eq(h)
38
+ expect(actual_result).to eq(exp_result)
39
+ end
40
+
41
+ it 'allows for inline key symbolization' do
42
+ h = { 'key' => { 'subkey1' => 'value1', 'subkey2' => { 'subsubkey1' => ['value1', 'value2']} } }
43
+ exp_result = { key: { subkey1: 'value1', subkey2: { subsubkey1: ['value1', 'value2']} } }
44
+ actual_result = h.deep_symbolize_keys!
45
+
46
+ expect(actual_result).to eq(h)
47
+ expect(actual_result).to eq(exp_result)
48
+ end
49
+
50
+ it 'can find a nested value by key' do
51
+ h = { key: { subkey1: 'value1', subkey2: { subsubkey1: 'subvalue1'} } }
52
+ h.recursive_find_by_key(:subsubkey1).should == 'subvalue1'
53
+ end
54
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/ext/logger_extensions'
3
+
4
+ describe Logger do
5
+ it 'allows custom levels to be attached' do
6
+ l = Logger.new(STDOUT)
7
+ l.formatter = proc do |severity, datetime, progname, msg|
8
+ puts "#{ severity }: #{ msg }"
9
+ end
10
+
11
+ out = capture_stdout { l.prompt('test') }
12
+ expect(out).to eq("PROMPT: test\n")
13
+
14
+ out = capture_stdout { l.section('test') }
15
+ expect(out).to eq("SECTION: test\n")
16
+
17
+ out = capture_stdout { l.success('test') }
18
+ expect(out).to eq("SUCCESS: test\n")
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../spec_helper'
2
+ require 'cliutils/ext/string_extensions'
3
+
4
+ describe String do
5
+ it 'outputs strings with preset custom colors' do
6
+ expect('blue string'.blue).to eq("\e[34mblue string\e[0m")
7
+ expect('cyan string'.cyan).to eq("\e[36mcyan string\e[0m")
8
+ expect('green string'.green).to eq("\e[32mgreen string\e[0m")
9
+ expect('purple string'.purple).to eq("\e[35mpurple string\e[0m")
10
+ expect('red string'.red).to eq("\e[31mred string\e[0m")
11
+ expect('white string'.white).to eq("\e[37mwhite string\e[0m")
12
+ expect('yellow string'.yellow).to eq("\e[33myellow string\e[0m")
13
+ end
14
+
15
+ it 'outputs strings with configurable custom colors' do
16
+ expect('crazy string'.colorize('34,42')).to eq("\e[34,42mcrazy string\e[0m")
17
+ end
18
+
19
+ it 'camel-cases strings' do
20
+ expect('my_long_snake_name'.camelize).to eq('MyLongSnakeName')
21
+ end
22
+
23
+ it 'snake-cases strings' do
24
+ expect('MyLongCamelName'.snakify).to eq('my_long_camel_name')
25
+ end
26
+ end