rbcli 0.2.1 → 0.2.2

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +13 -8
  5. data/Rakefile +5 -5
  6. data/bin/console +3 -3
  7. data/docs-src/docs/development/contributing.md +24 -0
  8. data/docs-src/docs/imported/changelog.md +23 -0
  9. data/docs-src/docs/imported/quick_reference.md +1 -0
  10. data/docs/development/contributing/index.html +15 -0
  11. data/docs/imported/changelog/index.html +120 -18
  12. data/docs/search/search_index.json +38 -8
  13. data/docs/sitemap.xml +19 -19
  14. data/lib/rbcli.rb +3 -22
  15. data/lib/rbcli/configuration/configurate.rb +49 -98
  16. data/lib/rbcli/{stateful_systems/configuratestorage.rb → configuration/configurate_blocks/hooks.rb} +23 -15
  17. data/lib/rbcli/configuration/configurate_blocks/me.rb +122 -0
  18. data/lib/rbcli/configuration/configurate_blocks/storage.rb +50 -0
  19. data/lib/rbcli/engine/command.rb +59 -60
  20. data/lib/rbcli/engine/parser.rb +17 -10
  21. data/lib/rbcli/{autoupdate → features/autoupdate/common}/autoupdate.rb +2 -20
  22. data/lib/rbcli/{autoupdate → features/autoupdate}/gem_updater.rb +1 -0
  23. data/lib/rbcli/{autoupdate → features/autoupdate}/github_updater.rb +1 -0
  24. data/lib/rbcli/{logging → features}/logging.rb +19 -18
  25. data/lib/rbcli/{remote_exec → features}/remote_exec.rb +0 -0
  26. data/lib/rbcli/{scriptwrapping → features}/scriptwrapper.rb +0 -29
  27. data/lib/rbcli/{configuration/config.rb → features/userconfig.rb} +0 -0
  28. data/lib/rbcli/{stateful_systems → state_storage/common}/state_storage.rb +0 -0
  29. data/lib/rbcli/{stateful_systems/storagetypes → state_storage}/localstate.rb +2 -10
  30. data/lib/rbcli/{stateful_systems/storagetypes → state_storage}/remote_state_connectors/dynamodb.rb +0 -0
  31. data/lib/rbcli/{stateful_systems/storagetypes → state_storage}/remotestate_dynamodb.rb +6 -20
  32. data/lib/rbcli/util/deprecation_warning.rb +43 -0
  33. data/lib/rbcli/version.rb +1 -1
  34. data/rbcli.gemspec +1 -1
  35. data/skeletons/micro/executable +5 -1
  36. data/skeletons/mini/executable +8 -8
  37. data/skeletons/project/Rakefile +2 -2
  38. data/skeletons/project/hooks/default_action.rb +1 -1
  39. data/skeletons/project/hooks/first_run.rb +1 -1
  40. data/skeletons/project/hooks/post_execution.rb +1 -1
  41. data/skeletons/project/hooks/pre_execution.rb +1 -1
  42. data/skeletons/project/spec/spec_helper.rb +2 -2
  43. metadata +17 -14
@@ -0,0 +1,122 @@
1
+ ##################################################################################
2
+ # RBCli -- A framework for developing command line applications in Ruby #
3
+ # Copyright (C) 2018 Andrew Khoury #
4
+ # #
5
+ # This program is free software: you can redistribute it and/or modify #
6
+ # it under the terms of the GNU General Public License as published by #
7
+ # the Free Software Foundation, either version 3 of the License, or #
8
+ # (at your option) any later version. #
9
+ # #
10
+ # This program is distributed in the hope that it will be useful, #
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of #
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
13
+ # GNU General Public License for more details. #
14
+ # #
15
+ # You should have received a copy of the GNU General Public License #
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>. #
17
+ # #
18
+ # For questions regarding licensing, please contact andrew@blacknex.us #
19
+ ##################################################################################
20
+
21
+
22
+ module Rbcli::Configurate::Me
23
+ include Rbcli::Configurable
24
+
25
+ @data = {
26
+ scriptname: nil,
27
+ version: nil,
28
+ description: nil,
29
+ config_userfile: nil,
30
+ options: {},
31
+ remote_execution: false,
32
+ autoupdater: nil
33
+ }
34
+ def self.data; @data; end
35
+
36
+ def self.scriptname name
37
+ @data[:scriptname] = name
38
+ end
39
+
40
+ def self.version vsn
41
+ @data[:version] = vsn
42
+ end
43
+
44
+ def self.description desc
45
+ @data[:description] = desc
46
+ end
47
+
48
+ def self.log_level level
49
+ Rbcli::Logger::save_defaults level: level
50
+ end
51
+
52
+ def self.log_target target
53
+ Rbcli::Logger::save_defaults target: target
54
+ end
55
+
56
+ def self.config_userfile *params
57
+ Rbcli::Config::set_userfile *params
58
+ @data[:config_userfile] = params[0]
59
+ end
60
+
61
+ def self.config_defaults filename
62
+ Rbcli::Config::add_defaults filename
63
+ end
64
+
65
+ def self.config_default *params
66
+ Rbcli::Config::add_default *params
67
+ end
68
+
69
+ def self.option name, description, short: nil, type: :boolean, default: nil, required: false, permitted: nil
70
+ default ||= false if (type == :boolean || type == :bool || type == :flag)
71
+ @data[:options][name.to_sym] = {
72
+ description: description,
73
+ type: type,
74
+ default: default,
75
+ required: required,
76
+ permitted: permitted,
77
+ short: short
78
+ }
79
+ end
80
+
81
+ def self.default_action &block
82
+ Rbcli::DeprecationWarning.new 'Rbcli::Configurate.me--default_action', 'Please use `RBCli::Configurate.hooks` as the parent block instead.', '0.3.0'
83
+ @data[:default_action] = block
84
+ end
85
+
86
+ def self.pre_hook &block
87
+ Rbcli::DeprecationWarning.new 'Rbcli::Configurate.me--pre_hook', 'Please use `RBCli::Configurate.hooks` as the parent block instead.', '0.3.0'
88
+ @data[:pre_hook] = block
89
+ end
90
+
91
+ def self.post_hook &block
92
+ Rbcli::DeprecationWarning.new 'Rbcli::Configurate.me--post_hook', 'Please use `RBCli::Configurate.hooks` as the parent block instead.', '0.3.0'
93
+ @data[:post_hook] = block
94
+ end
95
+
96
+ def self.first_run halt_after_running: false, &block
97
+ Rbcli::DeprecationWarning.new 'Rbcli::Configurate.me--first_run', 'Please use `RBCli::Configurate.hooks` as the parent block instead.', '0.3.0'
98
+ @data[:halt_after_first_run] = halt_after_running
99
+ @data[:first_run] = block
100
+ end
101
+
102
+ def self.remote_execution permitted: true
103
+ require 'rbcli/features/remote_exec' if permitted
104
+ @data[:remote_execution] = permitted
105
+ end
106
+
107
+ def self.autoupdate gem: nil, github_repo: nil, access_token: nil, enterprise_hostname: nil, force_update: false, message: nil
108
+ raise StandardError.new "Autoupdater can not have both a gem and git target defined. Please pick one." if gem and github_repo
109
+ raise StandardError.new "Only one autoupdater can be defined." if @data[:autoupdater]
110
+ if gem
111
+ require 'rbcli/features/autoupdate/gem_updater'
112
+ #Rbcli::Autoupdate::GemUpdater.save_defaults
113
+ @data[:autoupdater] = Rbcli::Autoupdate::GemUpdater.new gem, force_update, message
114
+ else
115
+ require 'rbcli/features/autoupdate/github_updater'
116
+ Rbcli::Autoupdate::GithubUpdater.save_defaults
117
+ @data[:autoupdater] = Rbcli::Autoupdate::GithubUpdater.new github_repo, access_token, enterprise_hostname, force_update, message
118
+ end
119
+ @data[:autoupdater].show_message if @data[:autoupdater].update_available?
120
+ end
121
+
122
+ end
@@ -0,0 +1,50 @@
1
+ ##################################################################################
2
+ # RBCli -- A framework for developing command line applications in Ruby #
3
+ # Copyright (C) 2018 Andrew Khoury #
4
+ # #
5
+ # This program is free software: you can redistribute it and/or modify #
6
+ # it under the terms of the GNU General Public License as published by #
7
+ # the Free Software Foundation, either version 3 of the License, or #
8
+ # (at your option) any later version. #
9
+ # #
10
+ # This program is distributed in the hope that it will be useful, #
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of #
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
13
+ # GNU General Public License for more details. #
14
+ # #
15
+ # You should have received a copy of the GNU General Public License #
16
+ # along with this program. If not, see <https://www.gnu.org/licenses/>. #
17
+ # #
18
+ # For questions regarding licensing, please contact andrew@blacknex.us #
19
+ ##################################################################################
20
+
21
+ module Rbcli::Configurate::Storage
22
+ include Rbcli::Configurable
23
+
24
+ @data = {
25
+ localstate: nil,
26
+ remotestate: nil,
27
+ remotestate_init_params: nil
28
+ }
29
+
30
+ def self.data; @data; end
31
+
32
+ def self.local_state path, force_creation: false, halt_on_error: false
33
+ require 'rbcli/state_storage/localstate'
34
+
35
+ @data[:localstate] = Rbcli::State::LocalStorage.new(path, force_creation: force_creation, halt_on_error: halt_on_error)
36
+ end
37
+
38
+ def self.remote_state_dynamodb table_name: nil, region: nil, force_creation: false, halt_on_error: true, locking: false
39
+ raise StandardError "Must decalre `table_name` and `region` to use remote_state_dynamodb" if table_name.nil? or region.nil?
40
+
41
+ require 'rbcli/state_storage/remotestate_dynamodb'
42
+
43
+ @data[:remotestate_init_params] = {
44
+ dynamodb_table: table_name,
45
+ region: region,
46
+ locking: locking
47
+ }
48
+ @data[:remotestate] = Rbcli::State::DynamoDBStorage.new(table_name, force_creation: force_creation, halt_on_error: halt_on_error)
49
+ end
50
+ end
@@ -19,41 +19,52 @@
19
19
  ##################################################################################
20
20
 
21
21
 
22
- class Rbcli::Command
23
-
24
- #include InheritableTraits
25
- #traits :description
26
-
27
- @commands = {}
22
+ module Rbcli::CmdLibrary
23
+ def self.extended klass
24
+ klass.instance_variable_set :@commands, {}
25
+ end
28
26
 
29
- def self.inherited subklass
27
+ def inherited subklass
28
+ subklass.instance_variable_set :@data, {
29
+ description: nil,
30
+ usage: nil,
31
+ action: nil,
32
+ paramlist: {},
33
+ remote_permitted: false
34
+ }
30
35
  @commands[subklass.name.downcase] = subklass.new
31
36
  end
32
37
 
33
- def self.add_command name, klass
34
- @commands[name.downcase] = klass.new
35
- end
38
+ def data; self.instance_variable_get :@data; end
36
39
 
37
- def self.commands
40
+ def commands
38
41
  @commands
39
42
  end
40
43
 
41
- ##
42
- # Interface Functions
43
- ##
44
- def self.description desc; @desc = desc end
45
- def description; self.class.instance_variable_get :@desc end
44
+ end
46
45
 
47
- def self.usage usage; @usage = usage end
48
- def usage; self.class.instance_variable_get :@usage end
49
46
 
50
- def self.action &block; @action = block end
51
- def action; self.class.instance_variable_get :@action end
47
+ class Rbcli::Command
48
+
49
+ #include InheritableTraits
50
+ #traits :description
51
+ extend Rbcli::CmdLibrary
52
52
 
53
+ def data; self.class.data; end
54
+
55
+ ##
56
+ # Interface Functions
57
+ ##
58
+ def self.description desc; @data[:description] = desc; end
59
+ def self.usage usage; @data[:usage] = usage; end
60
+ def self.action &block; @data[:action] = block; end
61
+ def self.remote_permitted; @data[:remote_permitted] = true; end
62
+ def self.remote_permitted?; @data[:remote_permitted]; end
63
+ def self.config_defaults filename; Rbcli::Config::add_defaults(filename); end
64
+ def self.config_default *params; Rbcli::Config::add_default *params; end
53
65
  def self.parameter name, description, short: nil, type: :boolean, default: nil, required: false, permitted: nil
54
66
  default ||= false if (type == :boolean || type == :bool || type == :flag)
55
- @paramlist ||= {}
56
- @paramlist[name.to_sym] = {
67
+ @data[:paramlist][name.to_sym] = {
57
68
  description: description,
58
69
  type: type,
59
70
  default: default,
@@ -62,24 +73,25 @@ class Rbcli::Command
62
73
  short: short
63
74
  }
64
75
  end
65
- def paramlist; self.class.instance_variable_get :@paramlist end
66
-
67
- def self.config_defaults filename
68
- Rbcli::Config::add_defaults filename
69
- end
70
-
71
- def self.config_default *params
72
- Rbcli::Config::add_default *params
73
- end
74
76
 
75
- def self.remote_permitted
76
- @remote_permitted = true
77
+ def self.extern path: nil, envvars: nil, &block
78
+ if path == :default
79
+ callerscript = caller_locations.first.absolute_path
80
+ path = "#{File.dirname(callerscript)}/scripts/#{File.basename(callerscript, ".*")}.sh"
81
+ end
82
+ block = nil unless block_given?
83
+ require 'rbcli/features/scriptwrapper'
84
+ @data[:extern] = Rbcli::Scriptwrapper.new path, envvars, block
77
85
  end
78
86
 
79
- def remote_permitted?
80
- self.class.instance_variable_get :@remote_permitted
87
+ def self.script path: nil, envvars: nil
88
+ if path == :default or path.nil?
89
+ callerscript = caller_locations.first.absolute_path
90
+ path = "#{File.dirname(callerscript)}/scripts/#{File.basename(callerscript, ".*")}.sh"
91
+ end
92
+ require 'rbcli/features/scriptwrapper'
93
+ @data[:script] = Rbcli::Scriptwrapper.new path, envvars, nil, true
81
94
  end
82
-
83
95
  ##
84
96
  # END Interface Functions
85
97
  ##
@@ -93,7 +105,7 @@ class Rbcli::Command
93
105
  global_opts = cliopts
94
106
  config = Rbcli::config
95
107
 
96
- raise Exception.new("Command #{cmd} can only have one of `action`, `script`, or `extern` defined.") if (@commands[cmd].extern or @commands[cmd].script) and @commands[cmd].action
108
+ raise Exception.new("Command #{cmd} can only have one of `action`, `script`, or `extern` defined.") if (@commands[cmd].data[:extern] or @commands[cmd].data[:script]) and @commands[cmd].data[:action]
97
109
 
98
110
  if cliopts[:remote_exec]
99
111
  Rbcli::RemoteExec.new(@commands[cmd], cliopts[:remote_exec], cliopts[:identity], params, args, global_opts, config).run
@@ -101,9 +113,9 @@ class Rbcli::Command
101
113
  return
102
114
  end
103
115
 
104
- @commands[cmd].extern.execute params, args, global_opts, config unless @commands[cmd].extern.nil?
105
- @commands[cmd].script.execute params, args, global_opts, config unless @commands[cmd].script.nil?
106
- @commands[cmd].action.call params, args, global_opts, config unless @commands[cmd].action.nil?
116
+ @commands[cmd].data[:extern].execute params, args, global_opts, config unless @commands[cmd].data[:extern].nil?
117
+ @commands[cmd].data[:script].execute params, args, global_opts, config unless @commands[cmd].data[:script].nil?
118
+ @commands[cmd].data[:action].call params, args, global_opts, config unless @commands[cmd].data[:action].nil?
107
119
  end
108
120
 
109
121
  ##
@@ -113,7 +125,7 @@ class Rbcli::Command
113
125
  #descmap = @commands.map { |name, klass| [name, klass.description] }.to_h
114
126
  @commands.map do |name, cmdobj|
115
127
  desc = ''
116
- if Rbcli.configuration[:remote_execution] and cmdobj.remote_permitted?
128
+ if Rbcli.configuration(:me, :remote_execution) and cmdobj.remote_permitted?
117
129
  indent_size -= 3
118
130
  indent_size.times { desc << ' ' }
119
131
  desc << '* '
@@ -121,20 +133,20 @@ class Rbcli::Command
121
133
  indent_size.times { desc << ' ' }
122
134
  end
123
135
  desc << name.ljust(justification)
124
- desc << cmdobj.description
136
+ desc << cmdobj.class.data[:description]
125
137
  end.join("\n")
126
138
  end
127
139
 
128
140
  ##
129
141
  # This method reads the parameters provided by the class and parses them from the CLI
130
142
  ##
131
- def parseopts *args
132
- params = paramlist
143
+ def self.parseopts *args
144
+ params = @data[:paramlist]
133
145
  command_name = self.class.name.split('::')[-1].downcase
134
- command_desc = description
135
- command_usage = usage
146
+ command_desc = @data[:description]
147
+ command_usage = @data[:usage]
136
148
  optx = Trollop::options do
137
- data = Rbcli.configuration
149
+ data = Rbcli.configuration(:me)
138
150
  banner <<-EOS
139
151
  #{data[:description]}
140
152
  Selected Command:
@@ -153,19 +165,6 @@ Command-specific Parameters:
153
165
  optx
154
166
  end
155
167
 
156
- ##
157
- # Inject metadata into response
158
- ##
159
- # def self.wrap_metadata resp
160
- # {
161
- # meta: {
162
- # status: 'ok',
163
- # timestamp: (Time.now.to_f * 1000).floor
164
- # },
165
- # response: resp
166
- # }.deep_stringify!
167
- # end
168
-
169
168
  ####
170
169
  ### DEPRECATED
171
170
  ####
@@ -20,15 +20,18 @@
20
20
 
21
21
 
22
22
  #TODO: Change this once the changes have been merged into trollop gem proper
23
- require "rbcli/util/trollop"
23
+ require 'rbcli/util/trollop'
24
24
 
25
25
  module Rbcli::Parser
26
26
 
27
27
  @cliopts = nil
28
28
 
29
29
  def self.parse
30
+ # We show deprecation warnings before anything else, encouraging the developer to update their code.
31
+ Rbcli::DeprecationWarning.display_warnings
32
+
30
33
  @cliopts = Trollop::options do
31
- data = Rbcli.configuration
34
+ data = Rbcli.configuration(:me)
32
35
  version "#{data[:scriptname]} version: #{data[:version]}"
33
36
  banner <<-EOS
34
37
  #{data[:description]}
@@ -63,21 +66,24 @@ Commands:
63
66
  Rbcli::Config::generate_userconf @cliopts[:config_file]
64
67
  puts "User config generated at #{@cliopts[:config_file]} using default values."
65
68
  elsif @cmd[0].nil?
66
- if Rbcli.configuration[:default_action].nil?
69
+ default_action = Rbcli.configuration(:me, :default_action) || Rbcli.configuration(:hooks, :default_action)
70
+ if default_action.nil?
67
71
  Trollop::educate
68
72
  else
69
- Rbcli.configuration[:default_action].call @cliopts
73
+ default_action.call @cliopts
70
74
  end
71
75
  elsif Rbcli::Command.commands.key? @cmd[0]
72
- @cmd << Rbcli::Command.commands[@cmd[0]].parseopts
76
+ @cmd << Rbcli::Command.commands[@cmd[0]].class.parseopts
73
77
 
74
78
  if (@cliopts[:remote_exec_given] and not @cliopts[:identity_given]) or (not @cliopts[:remote_exec_given] and @cliopts[:identity_given])
75
79
  Trollop::die 'Must use `--remote-exec` and `--identity` together.'
76
80
  end
77
81
 
78
- Rbcli.configuration[:pre_hook].call @cliopts unless Rbcli.configuration[:pre_hook].nil?
82
+ Rbcli.configuration(:me, :pre_hook).call @cliopts unless Rbcli.configuration(:me, :pre_hook).nil?
83
+ Rbcli.configuration(:hooks, :pre_hook).call @cliopts unless Rbcli.configuration(:hooks, :pre_hook).nil?
79
84
  Rbcli::Command.runcmd(@cmd.shift, @cmd[0], @cliopts)
80
- Rbcli.configuration[:post_hook].call @cliopts unless Rbcli.configuration[:post_hook].nil?
85
+ Rbcli.configuration(:me, :post_hook).call @cliopts unless Rbcli.configuration(:me, :post_hook).nil?
86
+ Rbcli.configuration(:hooks, :post_hook).call @cliopts unless Rbcli.configuration(:hooks, :post_hook).nil?
81
87
  else
82
88
  Trollop::die "Unknown subcommand #{@cmd[0].inspect}"
83
89
  end
@@ -88,14 +94,15 @@ end
88
94
 
89
95
  module Rbcli
90
96
  def self.parse
91
- if Rbcli.configuration[:first_run]
97
+ first_run_hook = Rbcli.configuration(:me, :first_run) || Rbcli.configuration(:hooks, :first_run)
98
+ if first_run_hook
92
99
  if Rbcli.local_state
93
100
  if Rbcli.local_state.rbclidata.key? :first_run
94
101
  Rbcli::Parser::parse
95
102
  else
96
- Rbcli.configuration[:first_run].call
103
+ first_run_hook.call
97
104
  Rbcli.local_state.set_rbclidata :first_run, true
98
- Rbcli::Parser::parse unless Rbcli.configuration[:halt_after_first_run]
105
+ Rbcli::Parser::parse unless Rbcli.configuration(:me, :halt_after_first_run) or Rbcli.configuration(:hooks, :halt_after_first_run)
99
106
  end
100
107
  else
101
108
  raise StandardError.new "Error: Can not use `first_run` without also configuring `local_state`."
@@ -18,21 +18,6 @@
18
18
  # For questions regarding licensing, please contact andrew@blacknex.us #
19
19
  ##################################################################################
20
20
 
21
- module Rbcli::Configurate
22
- def self.autoupdate gem: nil, github_repo: nil, access_token: nil, enterprise_hostname: nil, force_update: false, message: nil
23
- raise StandardError.new "Autoupdater can not have both a gem and git target defined. Please pick one." if gem and github_repo
24
- raise StandardError.new "Only one autoupdater can be defined." if @data[:autoupdater]
25
- if gem
26
- #Rbcli::Autoupdate::GemUpdater.save_defaults
27
- @data[:autoupdater] = Rbcli::Autoupdate::GemUpdater.new gem, force_update, message
28
- else
29
- Rbcli::Autoupdate::GithubUpdater.save_defaults
30
- @data[:autoupdater] = Rbcli::Autoupdate::GithubUpdater.new github_repo, access_token, enterprise_hostname, force_update, message
31
- end
32
- @data[:autoupdater].show_message if @data[:autoupdater].update_available?
33
- end
34
- end
35
-
36
21
 
37
22
  require 'rubygems'
38
23
  module Rbcli::Autoupdate
@@ -47,11 +32,11 @@ module Rbcli::Autoupdate
47
32
 
48
33
  def update_available?
49
34
  @latest_version = get_latest_version
50
- Gem::Version.new(@latest_version) > Gem::Version.new(Rbcli.configuration[:version])
35
+ Gem::Version.new(@latest_version) > Gem::Version.new(Rbcli.configuration(:me, :version))
51
36
  end
52
37
 
53
38
  def show_message
54
- puts "WARNING: An update is available to #{Rbcli::Configurate::configuration[:scriptname]}. You are currently running version #{Rbcli.configuration[:version]}; the latest is #{@latest_version || get_latest_version}."
39
+ puts "WARNING: An update is available to #{Rbcli.configuration(:me, :scriptname)}. You are currently running version #{Rbcli.configuration(:me, :version)}; the latest is #{@latest_version || get_latest_version}."
55
40
  puts @message || update_message
56
41
  puts "\n"
57
42
  if @force_update
@@ -61,6 +46,3 @@ module Rbcli::Autoupdate
61
46
  end
62
47
  end
63
48
  end
64
-
65
- require "rbcli/autoupdate/gem_updater"
66
- require "rbcli/autoupdate/github_updater"