rbcli 0.1.7 → 0.1.8

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +50 -6
  4. data/exe/rbcli +81 -32
  5. data/lib/rbcli/configuration/config.rb +6 -6
  6. data/lib/rbcli/configuration/configurate.rb +1 -1
  7. data/lib/rbcli/engine/load_project.rb +19 -0
  8. data/lib/rbcli/engine/parser.rb +2 -2
  9. data/lib/rbcli/logging/logging.rb +7 -4
  10. data/lib/rbcli/stateful_systems/state_storage.rb +4 -0
  11. data/lib/rbcli/version.rb +1 -1
  12. data/lib/rbcli-tool/erb_renderer.rb +43 -0
  13. data/lib/rbcli-tool/mdless_fix.rb +386 -0
  14. data/lib/rbcli-tool/project.rb +87 -0
  15. data/lib/rbcli-tool.rb +16 -0
  16. data/lib/rbcli.rb +11 -10
  17. data/rbcli.gemspec +1 -0
  18. data/skeletons/micro/executable +123 -0
  19. data/skeletons/mini/executable +241 -0
  20. data/skeletons/project/.gitignore +11 -0
  21. data/skeletons/project/.rbcli +0 -0
  22. data/skeletons/project/.rspec +3 -0
  23. data/skeletons/project/CODE_OF_CONDUCT.md +74 -0
  24. data/skeletons/project/Gemfile +6 -0
  25. data/skeletons/project/README.md +31 -0
  26. data/skeletons/project/Rakefile +6 -0
  27. data/skeletons/project/application/commands/command.rb +31 -0
  28. data/skeletons/project/application/commands/scripts/script.sh +23 -0
  29. data/skeletons/project/application/options.rb +30 -0
  30. data/skeletons/project/config/autoupdate.rb +32 -0
  31. data/skeletons/project/config/logging.rb +19 -0
  32. data/skeletons/project/config/storage.rb +34 -0
  33. data/skeletons/project/config/userspace.rb +28 -0
  34. data/skeletons/project/config/version.rb +3 -0
  35. data/skeletons/project/default_user_configs/user_defaults.yml +6 -0
  36. data/skeletons/project/exe/executable +54 -0
  37. data/skeletons/project/hooks/default_action.rb +16 -0
  38. data/skeletons/project/hooks/first_run.rb +16 -0
  39. data/skeletons/project/hooks/post_execution.rb +14 -0
  40. data/skeletons/project/hooks/pre_execution.rb +14 -0
  41. data/skeletons/project/spec/spec_helper.rb +14 -0
  42. data/skeletons/project/spec/untitled_spec.rb +9 -0
  43. data/skeletons/project/untitled.gemspec +40 -0
  44. metadata +47 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6ab8b75c47014a1d8fc4ebfb019fd510d6878446c0b5e21b534331518779c69
4
- data.tar.gz: 76a749c4eedd63b9372abfb5cf742c418e440bf52c07cfb80e14e4890e19dad2
3
+ metadata.gz: eeb0aa5b07a4650d68356dabb3e0f185f7b54adaa8911902780ee66d079a1d83
4
+ data.tar.gz: 674ce261ee73a19d22667fbfc9906bddfcd1bda88b526939b171696089ef55d9
5
5
  SHA512:
6
- metadata.gz: 841009711e6ea7507da51246fa3f116c767cee17229775dfb4524e649ed766f20c20069728d426d675d26bdbea9338f0e1a26873fc5120ba63473607815440dd
7
- data.tar.gz: edeb6d1302757421d2db44b44c38e07765920a67cd1230c45689541b7a4d29b4b511fd3e3b7d2f08b8abd36b17bbb30c639f71501bfbd5248518b5e4d70cc253
6
+ metadata.gz: 20eee68c0d42e059fa1e767211941e4e998e7f7c8e9f6440356544a777fd5abc268215d1036b42be3170c1b090bc9d77b176c2ffc89ffa94850c31f888887b5d
7
+ data.tar.gz: 6272c53067831c7902f42be08532f82199cee8bbb8427bc9ca6c8e2028add6f81726cec0dd381bc056cf030ec067c7249ab406538b4b0fac81b6ba79f5cb2fa9
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbcli (0.1.7)
4
+ rbcli (0.1.8)
5
5
  aws-sdk-dynamodb (~> 1.6)
6
6
  colorize (~> 0.8)
7
7
  deep_merge (~> 1.2)
8
8
  macaddr (~> 1.7)
9
+ mdless (~> 0.0)
9
10
  octokit (~> 4.9)
10
11
  rufus-scheduler (~> 3.5)
11
12
 
@@ -37,6 +38,7 @@ GEM
37
38
  jmespath (1.4.0)
38
39
  macaddr (1.7.1)
39
40
  systemu (~> 2.6.2)
41
+ mdless (0.0.10)
40
42
  minitest (5.11.3)
41
43
  multipart-post (2.0.0)
42
44
  octokit (4.9.0)
data/README.md CHANGED
@@ -26,6 +26,8 @@ Some of its key features include:
26
26
 
27
27
  * __External Script Wrapping__: High-level wrapping for Bash scripts, or any other applcication you'd like to wrap into a command.
28
28
 
29
+ * __Project Structure and Generators__: Create a well-defined project directory structure which organizes your code and allows you to package and distribute your application as a Gem. Generators can also help speed up the process of creating new commands, scripts, and hooks!
30
+
29
31
 
30
32
  ## Installation
31
33
 
@@ -60,14 +62,16 @@ mytool show -l
60
62
  Note that all options and parameters will have both a short and long version of the parameter available for use.
61
63
 
62
64
 
63
- ## Getting Started
65
+ ## Getting Started (Lightweight)
66
+
67
+ For a lightweight skeleton that consists of a single file, use `rbcli init -t mini -n <projectname>`, or `rbcli init -t micro -n <projectname>` for an even more simplified one.
64
68
 
65
- Creating a new skeleton command is as easy as running `rbcli init <filename>`. It will have these key items:
69
+ These lightweight skeletons allow creating single-file applications/scripts using RBCli. They consolidate all of the options in the standard project format into these sections:
66
70
 
67
71
  * The configuration
68
72
  * Storage subsystem configuration (optional)
69
73
  * A command declaration
70
- * The parse command
74
+ * The parse command
71
75
 
72
76
  ### Configuration
73
77
 
@@ -84,7 +88,7 @@ Rbcli::Configurate.me do
84
88
 
85
89
  config_userfile '/etc/mytool/config.yml', merge_defaults: true, required: false # (Optional) Set location of user's config file. If merge_defaults=true, user settings override default settings, and if false, defaults are not loaded at all. If required=true, application will not run if file does not exist.
86
90
  config_defaults 'defaults.yml' # (Optional, Multiple) Load a YAML file as part of the default config. This can be called multiple times, and the YAML files will be merged. User config is generated from these
87
- config_default :myopt, description: 'Testing this', value: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
91
+ config_default :myopt, description: 'Testing this', default: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
88
92
 
89
93
  option :name, 'Give me your name', type: :string, default: 'Foo', required: false, permitted: ['Jack', 'Jill'] # (Optional, Multiple) Add a global CLI parameter. Supported types are :string, :boolean, :integer, :float, :date, and :io. Can be called multiple times.
90
94
 
@@ -147,7 +151,7 @@ class Test < Rbcli::Command
147
151
  parameter :force, 'Force testing', type: :boolean, default: false, required: false # (Optional, Multiple) Add a command-specific CLI parameter. Can be called multiple times
148
152
 
149
153
  config_defaults 'defaults.yml' # (Optional, Multiple) Load a YAML file as part of the default config. This can be called multiple times, and the YAML files will be merged. User config is generated from these
150
- config_default :myopt2, description: 'Testing this again', value: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
154
+ config_default :myopt2, description: 'Testing this again', default: true # (Optional, Multiple) Specify an individual configuration parameter and set a default value. These will also be included in generated user config
151
155
 
152
156
  extern path: 'env | grep "^__PARAMS\|^__ARGS\|^__GLOBAL\|^__CONFIG"', envvars: {MYVAR: 'some_value'} # (Required unless `action` defined) Runs a given application, with optional environment variables, when the user runs the command.
153
157
  extern envvars: {MY_OTHER_VAR: 'another_value'} do |params, args, global_opts, config| # Alternate usage. Supplying a block instead of a path allows us to modify the command based on the arguments and configuration supplied by the user.
@@ -202,7 +206,7 @@ The defaults chain allows you to specify sane defaults for your CLI tool through
202
206
  * DSL Statements
203
207
  * In the DSL, you can specify options individually by providing a name, description, and default value
204
208
  * This is good for simpler configuration, as the descriptions provided are written out as comments in the generated user config
205
- * `config_default :name, description: '<description_text>', value: <default_value>` in the DSL
209
+ * `config_default :name, description: '<description_text>', default: <default_value>` in the DSL
206
210
 
207
211
  Users can generate configs by running `yourclitool -g`. This will generate a config file at the tool's default location specified in the DSL. A specific location can be used via the `-c` parameter. You can test how this works by running `examples/mytool -c test.yml -g`.
208
212
 
@@ -276,6 +280,8 @@ Hash native methods:
276
280
 
277
281
  Additional methods:
278
282
 
283
+ * `commit`
284
+ * Every assignment to the top level of the hash will result in a write to disk (for example: `Rbcli.local_state[:yourkey] = 'foo'`). If you are manipulating nested hashes, you can trigger a save manually by calling `commit`.
279
285
  * `clear`
280
286
  * Resets the data back to an empty hash.
281
287
  * `refresh`
@@ -481,6 +487,44 @@ class Test < Rbcli::Command
481
487
  end
482
488
  ```
483
489
 
490
+ ## Project Structure and Generators
491
+
492
+ RBCli supports using predefined project structure, helping to organize all of the options and commands that you may use. It also
493
+
494
+ Creating a new project skeleton is as easy as running `rbcli init -n <projectname>`. It will create a folder under the currect directory using the name specified, allowing you to create a command that can be easily packaged and distributed as a gem.
495
+
496
+ The folder structure is as follows:
497
+
498
+ ```
499
+ <projectname>
500
+ |
501
+ |--- application
502
+ | |
503
+ | |--- commands
504
+ | |
505
+ | |---scripts
506
+ |
507
+ |--- config
508
+ |--- default_user_configs
509
+ |--- exe
510
+ |--- hooks
511
+ |--- spec
512
+ ```
513
+
514
+ It is highly recommended to __not__ create files in these folders manually, and to use the RBCli generators instead:
515
+
516
+ ```shell
517
+ rbcli command -n <name>
518
+ rbcli script -n <name>
519
+ rbcli userconf -n <name>
520
+ rbcli hook -t pre
521
+ rbcli hook -t post
522
+ rbcli hook -t default
523
+ rbcli hook -t runonce
524
+ ```
525
+
526
+ That said, this readme will provide you with the information required to do things manually if you so desire. More details on generators later.
527
+
484
528
 
485
529
  ## Development
486
530
 
data/exe/rbcli CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'rbcli'
3
+ require '../lib/rbcli'
4
4
 
5
5
  Rbcli::Configurate.me do
6
6
  scriptname __FILE__.split('/')[-1]
@@ -8,46 +8,95 @@ Rbcli::Configurate.me do
8
8
  description 'RBCli initialization tool'
9
9
  end
10
10
 
11
- class Init < Rbcli::Command
12
- description 'Initialize a skeleton RBCli executable.'
13
- usage 'This will generate a new file in the current folder'
14
- parameter :filename, 'Name of file to generate', type: :string, required: true
11
+ require '../lib/rbcli-tool'
15
12
 
16
- action do |params, args, global_opts, config|
17
- src = "#{File.dirname(__FILE__)}/../examples/mytool"
18
- dest = "#{Dir.pwd}/#{params[:filename]}"
19
- cp_file src, dest
13
+ class Docs < Rbcli::Command
14
+ description 'Show Documentation (Beta)'
15
+
16
+ action do
17
+ readme = "#{File.dirname(__FILE__)}/../README.md"
18
+ begin
19
+ CLIMarkdown::Converter.new([readme])
20
+ rescue Errno::EPIPE
21
+ # Empty
22
+ end
20
23
  end
21
24
  end
22
25
 
23
- class Script < Rbcli::Command
24
- description 'Initialize a skeleton bash script to use as an RBCli command.'
25
- usage 'This will generate a new file in the current folder.'
26
- parameter :filename, 'Name of file to generate', type: :string, required: true
26
+ class Init < Rbcli::Command
27
+ description 'Initialize a skeleton RBCli project.'
28
+ usage <<-EOF
29
+ This will generate a new project structure under the current directory. Use -t to specify the type:
30
+
31
+
32
+ Standard: A complete RBCli project structure. Recommended for most applications, it provides
33
+ a framework for organizing code and creating a gem to be installed/distributed.
34
+
35
+ Mini: A single-file RBCli project. All features are supported, but project structure is left to the developer.
36
+ Recommended for smaller applications, or for integrating RBCli into an existing application.
37
+
38
+ Micro: A single-file, minimal RBCli project. Similar to a mini project, but only the minimal required code is
39
+ generated. Recommended for rapid prototyping of scripts, for advanced users only.
40
+ EOF
41
+
42
+ parameter :name, 'Name of project to generate', type: :string, required: true
43
+ parameter :type, 'Specify project type', type: :string, permitted: %w(standard micro mini), default: 'standard'
44
+ parameter :description, 'A description of the project', type: :string, default: 'TODO: Description goes here'
27
45
 
28
46
  action do |params, args, global_opts, config|
29
- src = "#{File.dirname(__FILE__)}/../examples/myscript.sh"
30
- dest = "#{Dir.pwd}/#{params[:filename]}"
31
- cp_file src, dest, template_vars: {'CMDNAME' => params[:filename].chomp('.sh')}
32
- end
33
- end
47
+ dest = "#{Dir.pwd}/#{params[:name]}"
48
+
49
+ # Prepare template vars
50
+ template_vars = {
51
+ cmdname: params[:name],
52
+ description: params[:description],
53
+ rbcli_version: Rbcli::VERSION
54
+ }
55
+
56
+ proj = RBCliTool::Project.new(dest, template_vars)
34
57
 
35
- def cp_file src, dest, template_vars: nil
36
- if File.exists? dest
37
- puts "File #{dest} already exists. Please delete it and try again."
38
- elsif template_vars
39
- print "Generating file #{dest}..."
40
- text = File.read src
41
- template_vars.each do |k, v|
42
- text.gsub! /{{\*\*#{k}\*\*}}/, v
58
+ if proj.exists?
59
+ RBCliTool.continue_confirmation "The project or file #{params[:name]} already exists; contents will be overwritten."
60
+ FileUtils.rm_rf dest
43
61
  end
44
- File.open(dest, 'w') { |file| file.write text }
45
- puts "Done!"
46
- else
47
- print "Generating file #{dest}..."
48
- FileUtils.cp src, dest
49
- puts "Done!"
62
+
63
+ case params[:type]
64
+ when 'micro' # Micro: Single File, simplified
65
+ puts "\nInitialization Complete!\n" if proj.create_micro
66
+
67
+ when 'mini' # Mini: Single File
68
+ puts "\nInitialization Complete!\n" if proj.create_mini
69
+
70
+ else # Standard; full project structure
71
+ if proj.create
72
+ puts "\nInitialization Complete!\n"
73
+ else
74
+ puts "\nAn RBCli Project already exists in the current directory tree at: #{proj.exists?}. Aborting.\n"
75
+ end
76
+
77
+ end # END case params[:type]
78
+
50
79
  end
51
80
  end
52
81
 
82
+ class Command < Rbcli::Command
83
+ description 'Generate an Rbcli Command under the current project'
84
+ usage <<-EOF
85
+ This will generate a new project structure under the current directory. Use -t to specify the type:
86
+
87
+
88
+ Standard: A complete RBCli project structure. Recommended for most applications, it provides
89
+ a framework for organizing code and creating a gem to be installed/distributed.
90
+
91
+ Mini: A single-file RBCli project. All features are supported, but project structure is left to the developer.
92
+ Recommended for smaller applications, or for integrating RBCli into an existing application.
93
+
94
+ Micro: A single-file, minimal RBCli project. Similar to a mini project, but only the minimal required code is
95
+ generated. Recommended for rapid prototyping of scripts, for advanced users only.
96
+ EOF
97
+
98
+ parameter :name, 'Name of command to generate', type: :string, required: true
99
+ end
100
+
101
+
53
102
  Rbcli.parse
@@ -45,7 +45,7 @@ module Rbcli::Config
45
45
  @categorized_defaults = nil
46
46
  @loaded = false
47
47
 
48
- def self.set_userfile filename, merge_defaults: false, required: false
48
+ def self.set_userfile filename, merge_defaults: true, required: false
49
49
  @config_file = File.expand_path filename
50
50
  @merge_defaults = merge_defaults
51
51
  @userfile_required = required
@@ -67,11 +67,11 @@ module Rbcli::Config
67
67
  @loaded = false
68
68
  end
69
69
 
70
- def self.add_default name, description: nil, value: nil
70
+ def self.add_default name, description: nil, default: nil
71
71
  @config_individual_lines ||= []
72
- text = "#{name.to_s}: #{value}".ljust(30) + " # #{description}"
72
+ text = "#{name.to_s}: #{default}".ljust(30) + " # #{description}"
73
73
  @config_individual_lines.push text unless @config_individual_lines.include? text
74
- @config_defaults[name.to_sym] = value
74
+ @config_defaults[name.to_sym] = default
75
75
  @loaded = false
76
76
  end
77
77
 
@@ -81,8 +81,8 @@ module Rbcli::Config
81
81
  @config_text ||= ''
82
82
  @config_text += "\n" unless @config_text.empty?
83
83
  File.readlines(filename).each do |line|
84
- if (line.start_with? '---' or line.start_with? '...')
85
- @config_text << "\n\n"
84
+ if line.start_with? '---' or line.start_with? '...'
85
+ @config_text << "\n" unless @config_text.empty?
86
86
  else
87
87
  @config_text << line unless @config_text.include? line
88
88
  end
@@ -4,7 +4,7 @@ module Rbcli::Configurate
4
4
  scriptname: nil,
5
5
  version: nil,
6
6
  description: nil,
7
- config_userfile: 'config.yml',
7
+ config_userfile: nil,
8
8
  allow_json: false,
9
9
  options: {},
10
10
  default_action: nil,
@@ -0,0 +1,19 @@
1
+ module Rbcli
2
+ def self.load_project
3
+ project_root = File.expand_path "#{File.dirname(caller[0].split(':')[0])}/../"
4
+
5
+ # We require all ruby files as-is
6
+ %w(config hooks application application/commands).each do |dir|
7
+ dirpath = "#{project_root}/#{dir}"
8
+ Dir.glob "#{dirpath}/*.rb" do |file|
9
+ require file
10
+ end if Dir.exists? dirpath
11
+ end
12
+
13
+ # We automatically pull in default user configs
14
+ configspath = "#{project_root}/default_user_configs"
15
+ Dir.glob "#{configspath}/*.{yml,yaml}" do |file|
16
+ Rbcli::Configurate.me {config_defaults file}
17
+ end
18
+ end
19
+ end
@@ -28,8 +28,8 @@ Commands:
28
28
  opt name.to_sym, opts[:description], type: opts[:type], default: opts[:default], required: opts[:required], permitted: opts[:permitted]
29
29
  end
30
30
  opt :json_output, 'Output result in machine-friendly JSON format', :type => :boolean, :default => false if data[:allow_json]
31
- opt :config_file, 'Specify a config file manually', :type => :string, :default => data[:config_userfile]
32
- opt :generate_config, 'Generate a new config file' #defaults to false
31
+ opt :config_file, 'Specify a config file manually', :type => :string, :default => data[:config_userfile] unless data[:config_userfile].nil?
32
+ opt :generate_config, 'Generate a new config file' unless data[:config_userfile].nil? #defaults to false
33
33
  stop_on Rbcli::Command.commands.keys
34
34
  end
35
35
 
@@ -29,12 +29,12 @@ module Rbcli::Logger
29
29
 
30
30
  Rbcli::Config::add_categorized_defaults :logger, 'Log Settings', {
31
31
  log_level: {
32
- description: '0-5, or DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN',
33
- value: @default_level || 'info'
32
+ description: '0-5, or DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN. Set to null (~) to disable logging.',
33
+ value: @default_level || nil
34
34
  },
35
35
  log_target: {
36
- description: 'STDOUT, STDERR, or a file path',
37
- value: @default_target || 'stderr'
36
+ description: 'STDOUT, STDERR, or a file path. Set to null (~) to disable logging.',
37
+ value: @default_target || nil
38
38
  }
39
39
  }
40
40
  end
@@ -45,9 +45,12 @@ module Rbcli::Logger
45
45
  target = STDOUT
46
46
  elsif Rbcli::config[:logger][:log_target].downcase == 'stderr'
47
47
  target = STDERR
48
+ elsif Rbcli::config[:logger][:log_target].nil?
49
+ target = '/dev/null'
48
50
  else
49
51
  target = Rbcli::config[:logger][:log_target]
50
52
  end
53
+ target = '/dev/null' if Rbcli::config[:logger][:log_level].nil?
51
54
  @logger = Logger.new(target)
52
55
  @logger.level = Rbcli::config[:logger][:log_level]
53
56
 
@@ -46,6 +46,10 @@ module Rbcli::State
46
46
  end
47
47
  end
48
48
 
49
+ def commit
50
+ save_state if @loaded
51
+ end
52
+
49
53
  def clear
50
54
  state_create unless @loaded
51
55
  @data[:data] = {}
data/lib/rbcli/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rbcli
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
@@ -0,0 +1,43 @@
1
+ require 'erb'
2
+
3
+ module RBCliTool
4
+ class ERBRenderer
5
+ def initialize filename, varlist
6
+ @filename = filename
7
+ @vars = varlist
8
+ end
9
+
10
+ def render
11
+ ERB.new(File.read(@filename)).result(binding)
12
+ end
13
+ end
14
+
15
+ def self.cp_file src, dest, template_vars = nil
16
+ dest = "#{dest}#{src.split('/')[-1]}" if dest.end_with? '/'
17
+ if File.exists? dest
18
+ puts "File #{dest} already exists. Please delete it and try again."
19
+ else
20
+ print "Generating file " + dest + " ... "
21
+
22
+ if template_vars
23
+ renderer = ERBRenderer.new src, template_vars
24
+ File.open(dest, 'w') {|file| file.write renderer.render}
25
+ else
26
+ FileUtils.cp src, dest
27
+ end
28
+
29
+ FileUtils.rm_f "#{File.dirname(dest)}/.keep" if File.exists? "#{File.dirname(dest)}/.keep"
30
+ puts "Done!"
31
+ end
32
+ end
33
+
34
+ def self.continue_confirmation text
35
+ puts text
36
+ print "Continue? (Y/n): "
37
+ input = gets
38
+ unless input[0].downcase == 'y'
39
+ puts "\n Aborting..."
40
+ exit 0
41
+ end
42
+ end
43
+ end