rbcli 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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"