rbcli 0.1.7 → 0.1.8

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