rbcli 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/Gemfile.lock +12 -12
  5. data/LICENSE.txt +674 -21
  6. data/README.md +80 -443
  7. data/bin/console +19 -0
  8. data/bin/setup +20 -0
  9. data/docs/404.html +639 -0
  10. data/docs/advanced/automatic_updates/index.html +791 -0
  11. data/docs/advanced/command_types/index.html +946 -0
  12. data/docs/advanced/distributed_state_locking/index.html +777 -0
  13. data/docs/advanced/hooks/index.html +836 -0
  14. data/docs/advanced/state_storage/index.html +957 -0
  15. data/docs/advanced/user_config_files/index.html +818 -0
  16. data/docs/assets/fonts/font-awesome.css +4 -0
  17. data/docs/assets/fonts/material-icons.css +13 -0
  18. data/docs/assets/fonts/specimen/FontAwesome.ttf +0 -0
  19. data/docs/assets/fonts/specimen/FontAwesome.woff +0 -0
  20. data/docs/assets/fonts/specimen/FontAwesome.woff2 +0 -0
  21. data/docs/assets/fonts/specimen/MaterialIcons-Regular.ttf +0 -0
  22. data/docs/assets/fonts/specimen/MaterialIcons-Regular.woff +0 -0
  23. data/docs/assets/fonts/specimen/MaterialIcons-Regular.woff2 +0 -0
  24. data/docs/assets/images/favicon.png +0 -0
  25. data/docs/assets/images/icons/bitbucket.1b09e088.svg +20 -0
  26. data/docs/assets/images/icons/github.f0b8504a.svg +18 -0
  27. data/docs/assets/images/icons/gitlab.6dd19c00.svg +38 -0
  28. data/docs/assets/javascripts/application.a59e2a89.js +1 -0
  29. data/docs/assets/javascripts/lunr/lunr.da.js +1 -0
  30. data/docs/assets/javascripts/lunr/lunr.de.js +1 -0
  31. data/docs/assets/javascripts/lunr/lunr.du.js +1 -0
  32. data/docs/assets/javascripts/lunr/lunr.es.js +1 -0
  33. data/docs/assets/javascripts/lunr/lunr.fi.js +1 -0
  34. data/docs/assets/javascripts/lunr/lunr.fr.js +1 -0
  35. data/docs/assets/javascripts/lunr/lunr.hu.js +1 -0
  36. data/docs/assets/javascripts/lunr/lunr.it.js +1 -0
  37. data/docs/assets/javascripts/lunr/lunr.jp.js +1 -0
  38. data/docs/assets/javascripts/lunr/lunr.multi.js +1 -0
  39. data/docs/assets/javascripts/lunr/lunr.no.js +1 -0
  40. data/docs/assets/javascripts/lunr/lunr.pt.js +1 -0
  41. data/docs/assets/javascripts/lunr/lunr.ro.js +1 -0
  42. data/docs/assets/javascripts/lunr/lunr.ru.js +1 -0
  43. data/docs/assets/javascripts/lunr/lunr.stemmer.support.js +1 -0
  44. data/docs/assets/javascripts/lunr/lunr.sv.js +1 -0
  45. data/docs/assets/javascripts/lunr/lunr.tr.js +1 -0
  46. data/docs/assets/javascripts/lunr/tinyseg.js +1 -0
  47. data/docs/assets/javascripts/modernizr.1aa3b519.js +1 -0
  48. data/docs/assets/stylesheets/application-palette.6079476c.css +2 -0
  49. data/docs/assets/stylesheets/application.ba0fd1a6.css +2 -0
  50. data/docs/development/code_of_conduct/index.html +883 -0
  51. data/docs/development/contributing/index.html +744 -0
  52. data/docs/development/license/index.html +715 -0
  53. data/docs/imported/changelog/index.html +853 -0
  54. data/docs/imported/quick_reference/index.html +1057 -0
  55. data/docs/index.html +732 -0
  56. data/docs/search/search_index.json +569 -0
  57. data/docs/sitemap.xml +93 -0
  58. data/docs/tutorial/10-getting_started/index.html +806 -0
  59. data/docs/tutorial/20-project_layout/index.html +972 -0
  60. data/docs/tutorial/30-your_first_command/index.html +906 -0
  61. data/docs/tutorial/40-options_parameters_and_arguments/index.html +1049 -0
  62. data/docs/tutorial/50-publishing/index.html +838 -0
  63. data/docs/whoami/index.html +709 -0
  64. data/docs-src/docs/advanced/automatic_updates.md +42 -0
  65. data/docs-src/docs/advanced/command_types.md +144 -0
  66. data/docs-src/docs/advanced/distributed_state_locking.md +33 -0
  67. data/docs-src/docs/advanced/hooks.md +65 -0
  68. data/docs-src/docs/advanced/logging.md +35 -0
  69. data/docs-src/docs/advanced/state_storage.md +117 -0
  70. data/docs-src/docs/advanced/user_config_files.md +47 -0
  71. data/docs-src/docs/development/code_of_conduct.md +74 -0
  72. data/docs-src/docs/development/contributing.md +49 -0
  73. data/docs-src/docs/development/license.md +10 -0
  74. data/docs-src/docs/imported/changelog.md +31 -0
  75. data/docs-src/docs/imported/quick_reference.md +150 -0
  76. data/docs-src/docs/index.md +38 -0
  77. data/docs-src/docs/tutorial/10-getting_started.md +41 -0
  78. data/docs-src/docs/tutorial/20-project_layout.md +115 -0
  79. data/docs-src/docs/tutorial/30-your_first_command.md +126 -0
  80. data/docs-src/docs/tutorial/40-options_parameters_and_arguments.md +251 -0
  81. data/docs-src/docs/tutorial/50-publishing.md +47 -0
  82. data/docs-src/docs/whoami.md +28 -0
  83. data/docs-src/makesite.sh +14 -0
  84. data/docs-src/mkdocs.yml +76 -0
  85. data/docs-src/runsite.sh +3 -0
  86. data/exe/rbcli +76 -5
  87. data/lib/rbcli/autoupdate/autoupdate.rb +24 -4
  88. data/lib/rbcli/autoupdate/gem_updater.rb +23 -2
  89. data/lib/rbcli/autoupdate/github_updater.rb +22 -1
  90. data/lib/rbcli/configuration/config.rb +22 -1
  91. data/lib/rbcli/configuration/configurate.rb +24 -2
  92. data/lib/rbcli/engine/command.rb +26 -6
  93. data/lib/rbcli/engine/load_project.rb +29 -3
  94. data/lib/rbcli/engine/parser.rb +25 -4
  95. data/lib/rbcli/logging/logging.rb +21 -0
  96. data/lib/rbcli/scriptwrapping/scriptwrapper.rb +30 -11
  97. data/lib/rbcli/stateful_systems/configuratestorage.rb +20 -0
  98. data/lib/rbcli/stateful_systems/state_storage.rb +20 -0
  99. data/lib/rbcli/stateful_systems/storagetypes/localstate.rb +20 -0
  100. data/lib/rbcli/stateful_systems/storagetypes/remote_state_connectors/dynamodb.rb +20 -0
  101. data/lib/rbcli/stateful_systems/storagetypes/remotestate_dynamodb.rb +20 -0
  102. data/lib/rbcli/util/hash_deep_symbolize.rb +43 -22
  103. data/lib/rbcli/util/string_colorize.rb +20 -0
  104. data/lib/rbcli/version.rb +21 -1
  105. data/lib/rbcli-tool/generators.rb +20 -0
  106. data/lib/rbcli-tool/mdless_fix.rb +20 -0
  107. data/lib/rbcli-tool/project.rb +27 -2
  108. data/lib/rbcli-tool/util.rb +20 -0
  109. data/lib/rbcli-tool.rb +20 -0
  110. data/lib/rbcli.rb +20 -0
  111. data/lib-sh/lib-rbcli.sh +19 -0
  112. data/rbcli.gemspec +22 -3
  113. data/skeletons/project/CODE_OF_CONDUCT.md +1 -1
  114. data/skeletons/project/README.md +17 -2
  115. data/skeletons/project/application/commands/command.erb +10 -8
  116. data/skeletons/project/application/commands/script.erb +3 -1
  117. data/skeletons/project/application/commands/scripts/script.sh +2 -2
  118. data/skeletons/project/application/options.rb +12 -3
  119. data/skeletons/project/config/autoupdate.rb +5 -2
  120. data/skeletons/project/config/storage.rb +7 -6
  121. data/skeletons/project/config/userspace.rb +6 -1
  122. data/skeletons/project/exe/executable +1 -1
  123. data/skeletons/project/lib/.keep +0 -0
  124. data/skeletons/project/untitled.gemspec +4 -4
  125. data/skeletons/project/{default_user_configs → userconf}/user_defaults.yml +0 -0
  126. metadata +85 -9
  127. data/examples/defaults.yml +0 -4
  128. data/examples/myscript.sh +0 -23
  129. data/examples/mytool +0 -95
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
- RBCli is currently in Alpha stages of development. All releases can be considered stable, though breaking changes may be made between versions.
1
+ RBCli is currently in Alpha stages of development. All releases can be considered stable, though breaking changes may be made between versions.
2
2
 
3
3
  # RBCli
4
4
 
5
- RBCli is a framework to quickly develop advanced command-line tools in Ruby. It has been written from the ground up with the needs of the modern technologist in mind, designed to make advanced CLI tool development as painless as possible.
5
+ As technologists today, we work with the command line a lot. We script a lot. We write tools to share with each other to make our lives easier. We even write applications to make up for missing features in the 3rd party software that we buy. Unfortunately, when writing CLI tools, this process has typically been very painful. We've been working with low-level frameworks for decades; frameworks like `getopt` (1980) and `curses` (1977). They fit their purpose well; they were both computationally lightweight for the computers of the day, and they gave engineers full control and flexibility when it came to how things were built. Over the years, we've used them to settle on several design patterns that we know work well. Patterns as to what a CLI command looks like, what a config file looks like, what remote execution looks like, and even how to use locks (mutexes, semaphores, etc) to control application flow and data atomicity. Yet we're stuck writing the same low-level code anytime we want to write our tooling. Not anymore.
6
+
7
+ Enter RBCli. RBCli is a framework to quickly develop advanced command-line tools in Ruby. It has been written from the ground up with the needs of the modern technologist in mind, designed to make advanced CLI tool development as painless as possible. In RBCli, low-level code has been wrapped and/or replaced with higher-level methods. Much of the functionality has even been reduced to single methods: for example, it takes just one declaration to define, load, and generate a user's config file at the appropriate times. Many other features are automated and require no work by the engineer. These make RBCli a fundamental re-thining of how we develop CLI tools, enabling the rapid development of applications for everyone from hobbyists to enterprises.
8
+
6
9
 
7
10
  Some of its key features include:
8
11
 
@@ -29,522 +32,156 @@ Some of its key features include:
29
32
  * __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
33
 
31
34
 
32
- ## Installation
33
-
34
- RBCli is available on rubygems.org. You can add it to your application's `Gemfile` or `gemspec`, or install it manually via `gem install rbcli`.
35
+ For more information, take a look at the __[official documentation](http://akhoury6.github.io/rbcli/)__ or keep reading for a quick reference.
35
36
 
36
37
 
37
- ## The Basics
38
+ # Quick Reference
38
39
 
39
- RBCli enforces a general command-line structure:
40
+ ## Installation
40
41
 
41
- ```
42
- toolname [options] command [parameters] [lineitem]
43
- ```
42
+ RBCli is available on rubygems.org. You can add it to your application's `Gemfile` or `gemspec`, or install it manually by running:
44
43
 
45
- * __Options__ are command line parameters such as `-f`, or `--force`. These are available globally to every command. You can create your own, but several options are already built-in:
46
- * `-c <filename> / --config-file=<filename>` allows specifying a config file manually
47
- * `-g / --generate-config` generates a config file by writing out the defaults to a YAML file (location is configurable, more on that below)
48
- * `-v / --version` shows the version`
49
- * `-h / --help` shows the help
50
- * __Command__ represents the subcommands that you will create, such as `test` or `apply`
51
- * __Parameters__ are the same as options, but only apply to the specific subcommand being executed. In this case only the `-h / --help` parameter is provided.
52
- * __Lineitems__ are strings that don't begin with a '-', and are passed to the command's code. These can be used as subcommands or additional parameters for your code.
53
-
54
- So a valid command could look something like these:
55
-
56
- ```shell
57
- mytool -n load --filename=foo.txt
58
- mytool parse foo.txt
59
- mytool show -l
44
+ ```bash
45
+ gem install rbcli
60
46
  ```
61
47
 
62
- Note that all options and parameters will have both a short and long version of the parameter available for use.
48
+ Then, `cd` to the folder you'd like to create your project under and run:
63
49
 
64
-
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.
68
-
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:
70
-
71
- * The configuration
72
- * Storage subsystem configuration (optional)
73
- * A command declaration
74
- * The parse command
75
-
76
- ### Configuration
77
-
78
- ```ruby
79
- require 'rbcli'
80
-
81
- Rbcli::Configurate.me do
82
- scriptname __FILE__.split('/')[-1] # (Required) This line identifies the tool's executable. You can change it if needed, but this should work for most cases.
83
- version '0.1.0' # (Required) The version number
84
- description 'This is my test CLI tool.' # (Requierd) A description that will appear when the user looks at the help with -h. This can be as long as needed.
85
-
86
- log_level :info # (Optional) Set the default log_level for users. 0-5, or DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
87
- log_target 'stderr' # (Optional) Set the target for logs. Valid values are STDOUT, STDERR, or a file path (as strings)
88
-
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.
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
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
92
-
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.
94
-
95
- autoupdate github_repo: 'akhoury6/rbcli', access_token: nil, enterprise_hostname: nil, force_update: false # (Optional) Check for updates to this application at a GitHub repo. The repo must use version number tags in accordance to best practices: https://help.github.com/articles/creating-releases/
96
- autoupdate gem: 'rbcli', force_update: false # (Optional) Check for updates to this application through RubyGems.org.
97
-
98
- default_action do |opts| # (Optional) The default code to execute when no subcommand is given. If not present, the help is shown (same as -h)
99
- puts "Hello, #{opts[:name]}."
100
- puts "To see the help, use -h"
101
- end
102
-
103
- pre_hook do |opts| # (Optional) Allows providing a block of code that runs before any command
104
- puts 'This is a pre-command hook. It executes before the command.'
105
- end
106
-
107
- post_hook do |opts| # (Optional) Allows providing a block of code that runs after any command
108
- puts 'This is a post-command hook. It executes after the command.'
109
- end
110
-
111
- first_run halt_after_running: true do # (Optional) Allows providing a block of code that executes the first time that the application is run on a given system. If `halt_after_running` is set to `true` then parsing will not continue after this code is executed. All subsequent runs will not execute this code.
112
- puts "This is the first time the mytool command is run! Don't forget to generate a config file with the `-g` option before continuing."
113
- end
114
- end
50
+ ```bash
51
+ rbcli init -n mytool -d "A simple CLI tool"
115
52
  ```
116
53
 
117
- #### CLI Option Declarations
54
+ Or, for a single-file tool without any folder/gem tructure, run `rbcli init -t mini -n <projectname>` or `rbcli init -t micro -n <projectname>`.
118
55
 
119
- For the `option` parameters that you want to create, the following types are supported:
120
56
 
121
- * `:string`
122
- * `:boolean` or `:flag`
123
- * `:integer`
124
- * `:float`
57
+ ## Creating a command
125
58
 
126
- If a default value is not set, it will default to `nil`.
59
+ There are three types of commands: standard, scripted, and external.
127
60
 
128
- If you want to declare more than one option, you can call it multiple times. The same goes for other items tagged with _Multiple_ in the description above.
61
+ * __Standard__ commands let you code the command directly in Ruby
62
+ * __Scripted__ commands provide you with a bash script, where all of the parsed information (params, options, args, and config) is shared
63
+ * __External__ commands let you wrap 3rd party applications directly
129
64
 
130
- Once parsed, options will be placed in a hash where they can be accessed via their names as shown above. You can see this demonstrated in the `default_action`, `pre_hook`, and `post_hook` blocks.
65
+ ### Standard Commands
131
66
 
132
- ### Storage Configuration (optional)
67
+ To create a new command called `foo`, run:
133
68
 
134
- ```ruby
135
- Rbcli::Configurate.storage do
136
- local_state '/var/mytool/localstate', force_creation: true, halt_on_error: true # (Optional) Creates a hash that is automatically saved to a file locally for state persistance. It is accessible to all commands at Rbcli.local_state[:yourkeyhere]
137
- remote_state_dynamodb table_name: 'mytable', region: 'us-east-1', force_creation: true, halt_on_error: true, locking: true # (Optional) Creates a hash that is automatically saved to a DynamoDB table. It is recommended to keep halt_on_error=true when using a shared state.
138
- end
69
+ ```bash
70
+ rbcli command -n foo
139
71
  ```
140
72
 
141
- This block configures different storage interfaces. For more details please see the [Storage Subsystems](#storage_subsystems) section below.
142
-
143
- ### Command Declaration
73
+ You will now find the command code in `application/commands/list.rb`. Edit the `action` block to write your coode.
144
74
 
145
- Commands are added by subclassing `Rbcli::Command`. There are a few parameters to set for it, which get used by the different components of Rbcli.
75
+ ### Scripted Commands
146
76
 
147
- ```ruby
148
- class Test < Rbcli::Command # Declare a new command by subclassing Rbcli::Command
149
- description 'This is a short description.' # (Required) Short description for the global help
150
- usage 'This is some really long usage text description!' # (Required) Long description for the command-specific help
151
- parameter :force, 'Force testing', type: :boolean, default: false, required: false # (Optional, Multiple) Add a command-specific CLI parameter. Can be called multiple times
152
-
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
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
155
-
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.
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.
158
- "echo #{params[:force].to_s}__YESSS!!!"
159
- end
160
-
161
- action do |params, args, global_opts, config| # (Required unless `extern` defined) Block to execute if the command is called.
162
- Rbcli::log.info { 'These logs can go to STDERR, STDOUT, or a file' } # Example log. Interface is identical to Ruby's logger
163
- puts "\nArgs:\n#{args}" # Arguments that came after the command on the CLI (i.e.: `mytool test bar baz` will yield args=['bar', 'baz'])
164
- puts "Params:\n#{params}" # Parameters, as described through the option statements above
165
- puts "Global opts:\n#{global_opts}" # Global Parameters, as descirbed in the Configurate section
166
- puts "Config:\n#{config}" # Config file values
167
- puts "LocalState:\n#{Rbcli.local_state}" # Local persistent state storage (when available) -- if unsure use Rbcli.local_state.nil?
168
- puts "RemoteState:\n#{Rbcli.remote_state}" # Remote persistent state storage (when available) -- if unsure use Rbcli.remote_state.nil?
169
- puts "\nDone!!!"
170
- end
171
- end
172
- ```
77
+ To create a new scripted command called `bar`, run:
173
78
 
174
- ### Parse Command
175
-
176
- ```ruby
177
- Rbcli.parse # Parse CLI and execute
79
+ ```bash
80
+ rbcli script -n bar
178
81
  ```
179
82
 
180
- ## Execution Chain
181
-
182
- RBCli takes actions in a specific order when `parse` is run.
183
-
184
- 1. The default config is loaded
185
- 2. The user config is loaded if present
186
- 3. CLI is parsed for global options, which are then stored in a hash and passed on to the other parts of the code. Built-in options, however, may cause the code to branch and exit.
187
- 4. The CLI is parsed for a command.
188
- a. If no command is entered, RBCLI will check if a `default_acction` block has been provided.
189
- i. If the block is defined, execute it
190
- ii. Otherwise, show the help
191
- b. If a command has been entered, the rest of the CLI is parsed for parameters and lineitems, and the code block for the command is called
192
-
193
-
194
- ## Configuration Files
195
-
196
- RBCli has two chains to manage configuration: the __defaults chain__ and the __user chain__.
197
-
198
- ### Defaults chain
83
+ You will then find two new files:
199
84
 
200
- The defaults chain allows you to specify sane defaults for your CLI tool throughout your code. This gives you the ability to declare configuration alongside the code, and allows RBCli to generate a user config automatically given your defaults. There are two ways to set them:
85
+ * The command declaration under `application/commands/bar.rb`
86
+ * The script code under `application/commands/scripts/bar.sh`
201
87
 
202
- * YAML Files
203
- * You can store your defaults in one or more YAML files and RBCli will import and combine them. Note that when generating the user config, RBCli will use the YAML text as-is, so comments are transferred as well. This allows you to write descriptions for the options directly in the file that the user can see.
204
- * This is good for tools with large or complex configuration that needs user documentation written inline
205
- * `config_defaults "<filename>"` in the DSL
206
- * DSL Statements
207
- * In the DSL, you can specify options individually by providing a name, description, and default value
208
- * This is good for simpler configuration, as the descriptions provided are written out as comments in the generated user config
209
- * `config_default :name, description: '<description_text>', default: <default_value>` in the DSL
210
-
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`.
212
-
213
- ### User chain
88
+ Edit the script to write your code.
214
89
 
215
- The user chain has two functions: generating and loading configuration from a YAML file.
90
+ ### External Commands
216
91
 
217
- Rbcli will determine the correct location to locate the user configuration based on two factors:
92
+ To create a new external command called `baz`, run:
218
93
 
219
- 1. The default location set in the configurate DSL
220
- a. `config_userfile '/etc/mytool/config.yml', merge_defaults: true, required: false`
221
- b. (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.
222
- 2. The location specified on the command line using the `-c <filename>` option
223
- b. `yourclitool -c localfile.yml`
224
-
225
- 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`.
226
-
227
-
228
- ## Logging
229
-
230
- Logging with RBCli is straightforward - it looks at the config file for logging settings, and instantiates a single, globally accessible [Logger](https://ruby-doc.org/stdlib-2.4.0/libdoc/logger/rdoc/Logger.html)` object. You can access it as such:
231
-
232
- ```ruby
233
- Rbcli::log.info { 'These logs can go to STDERR, STDOUT, or a file' }
234
- ```
235
-
236
- Default values can be set in the configurate DSL:
237
-
238
- ```ruby
239
- log_level :info # (Optional) Set the default log_level for users. 0-5, or DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
240
- log_target 'stderr' # (Optional) Set the target for logs. Valid values are STDOUT, STDERR, or a file path (as strings)
241
- ```
242
-
243
- Two configuration options will always be placed in a user's YAML file to override defaults:
244
-
245
- ```yaml
246
- # Log Settings
247
- logger:
248
- log_level: warn # 0-5, or DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
249
- log_target: stderr # STDOUT, STDERR, or a file path
94
+ ```bash
95
+ rbcli extern -n baz
250
96
  ```
251
97
 
98
+ You will then find the command code in `application/commands/baz.rb`.
252
99
 
253
- ## <a name="storage_subsystems"></a>Storage Subsystems
254
-
255
- ```ruby
256
- Rbcli::Configurate.storage do
257
- local_state '/var/mytool/localstate', force_creation: true, halt_on_error: true # (Optional) Creates a hash that is automatically saved to a file locally for state persistance. It is accessible to all commands at Rbcli.local_state[:yourkeyhere]
258
- remote_state_dynamodb table_name: 'mytable', region: 'us-east-1', force_creation: true, halt_on_error: true, locking: true # (Optional) Creates a hash that is automatically saved to a DynamoDB table. It is recommended to keep halt_on_error=true when using a shared state.
259
- end
260
- ```
100
+ Use one of the two provided modes -- direct path mode or variable path mode -- to provide the path to the external program.
261
101
 
262
- ### <a name="local_state"></a> Local State
263
102
 
264
- RBCli's local state storage gives you access to a hash that is automatically persisted to disk when changes are made.
103
+ ## Hooks
265
104
 
266
- Once configured you can access it with a standard hash syntax:
105
+ RBCli has several hooks that run at different points in the exectution chain. They can be created via the `rbcli` command line tool:
267
106
 
268
- ```ruby
269
- Rbcli.local_state[:yourkeyhere]
107
+ ```bash
108
+ rbcli hook --default # Runs when no command is provided
109
+ rbcli hook --pre # Runs before any command
110
+ rbcli hook --post # Runs after any command
111
+ rbcli hook --firstrun # Runs the first time a user runs your application. Requires userspace config.
112
+ rbcli hook -dpof # Create all hooks at once
270
113
  ```
271
114
 
272
- For performance reasons, the only methods available for use are:
115
+ ## Storage
273
116
 
274
- Hash native methods:
117
+ RBCli supports both local and remote state storage. This is done by synchronizing a Hash with either the local disk or a remote database.
275
118
 
276
- * `[]` (Regular hash syntax. Keys are accessed via either symbols or strings indifferently.)
277
- * `[]=` (Assignment operator)
278
- * `delete`
279
- * `each`
280
- * `key?`
119
+ ### Local State
281
120
 
282
- Additional methods:
121
+ RBCli can provide you with a unique hash that can be persisted to disk on any change to a top-level value.
283
122
 
284
- * `commit`
285
- * 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`.
286
- * `clear`
287
- * Resets the data back to an empty hash.
288
- * `refresh`
289
- * Loads the most current version of the data from the disk
290
- * `disconnect`
291
- * Removes the data from memory and sets `Rbcli.local_state = nil`. Data will be read from disk again on next access.
123
+ Enable local state in `config/storage.rb`.
292
124
 
125
+ Then access it in your Standard Commands with `Rbcli.local_state[:yourkeyhere]`.
293
126
 
294
- Every assignment will result in a write to disk, so if an operation will require a large number of assignments/writes it should be performed to a different hash before beign assigned to this one.
127
+ ### Remote State
295
128
 
296
- #### Configuration Parameters
129
+ Similar to the Local State above, RBCli can provide you with a unique hash that can be persisted to a remote storage location.
297
130
 
298
- ```ruby
299
- Rbcli::Configurate.storage do
300
- local_state '/var/mytool/localstate', force_creation: true, halt_on_error: true # (Optional) Creates a hash that is automatically saved to a file locally for state persistance. It is accessible to all commands at Rbcli.local_state[:yourkeyhere]
301
- end
302
- ```
131
+ Currently only AWS DynamoDB is supported, and credentials will be required for each user.
303
132
 
304
- There are three parameters to configure it with:
305
- * The `path` as a string (self-explanatory)
306
- * `force_creation`
307
- * This will attempt to create the path and file if it does not exist (equivalent to an `mkdir -p` and `touch` in linux)
308
- * `halt_on_error`
309
- * RBCli's default behavior is to raise an exception if the file can not be created, read, or updated at any point in time
310
- * If this is set to `false`, RBCli will silence any errors pertaining to file access and will fall back to whatever data is available. Note that if this is enabled, changes made to the state may not be persisted to disk.
311
- * If creation fails and file does not exist, you start with an empty hash
312
- * If file exists but can't be read, you will have an empty hash
313
- * If file can be read but not written, the hash will be populated with the data. Writes will be stored in memory while the application is running, but will not be persisted to disk.
133
+ Enable remote state in `config/storage.rb`.
314
134
 
315
- ### <a name="remote_state">Remote State
135
+ Then access it in your Standard Commands with `Rbcli.remote_state[:yourkeyhere]`.
316
136
 
317
- RBCli's remote state storage gives you access to a hash that is automatically persisted to a remote storage location when changes are made. It has locking built-in, meaning that multiple users may share remote state without any data consistency issues!
318
137
 
319
- Once configured you can access it with a standard hash syntax:
138
+ ## Userspace Configuration Files
320
139
 
321
- ```ruby
322
- Rbcli.remote_state[:yourkeyhere]
323
- ```
140
+ RBCli provides an easy mechanism to generate and read configuration files from your users. You set the default values and help text with the __defaults chain__, and leverage the __user chain__ to read them.
324
141
 
325
- This works the same way that [Local State](#local_state) does, with the same performance caveats (try not to do many writes!).
142
+ You can set defaults either by placing a YAML file in the `userconf/` folder or by specifying individual options in `application/options.rb` (global) or `application/command/*.rb` (command-specific).
326
143
 
327
- Note that all state in Rbcli is __lazy-loaded__, so no connections will be made until your code attempts to access the data.
144
+ Users can generate a config file, complete with help text, by running your tool with the `--generate-config` option.
328
145
 
329
- #### DynamoDB Configuration
330
146
 
331
- Before DynamoDB can be used, AWS API credentials have to be created and made available. RBCli will attempt to find credentials from the following locations in order:
332
-
333
- 1. User's config file
334
- 2. Environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
335
- 3. User's AWSCLI configuration at `~/.aws/credentials`
147
+ ## Logging
336
148
 
337
- For more information about generating and storing AWS credentials, see [Configuring the AWS SDK for Ruby](https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html).
149
+ RBCli's logger is configured in `config/logging.rb`.
338
150
 
339
151
  ```ruby
340
- Rbcli::Configurate.storage do
341
- remote_state_dynamodb table_name: 'mytable', region: 'us-east-1', force_creation: true, halt_on_error: true, locking: true # (Optional) Creates a hash that is automatically saved to a DynamoDB table. It is recommended to keep halt_on_error=true when using a shared state.
342
- end
152
+ log_level :info
153
+ log_target 'stderr'
343
154
  ```
344
155
 
345
- These are the parameters:
346
- * `table_name`
347
- * The name of the DynamoDB table to use.
348
- * `region`
349
- * The AWS region that the database is located
350
- * `force_creation`
351
- * Creates the DynamoDB table if it does not already exist
352
- * `halt_on_error`
353
- * Similar to the way [Local State](#local_state) works, setting this to `false` will silence any errors in connecting to the DynamoDB table. Instead, your application will simply have access to an empty hash that does not get persisted anywhere.
354
- * This is good for use cases that involve using this storage as a cache to "pick up where you left off in case of failure", where a connection error might mean the feature doesn't work but its not important enough to interrupt the user.
355
- * `locking`
356
- * This enables locking, for when you are sharing state between different instances of the application. For more information see the [section below](#distributed_locking).
357
-
358
- #### <a name="distributed_locking">Distributed Locking and State Sharing
359
-
360
- Distributed Locking allows a remote state to be shared among multiple users of the application without risk of data corruption. To use it, simply set the `locking:` parameter to `true` when enabling remote state (see above).
361
-
362
- This is how locking works:
363
-
364
- 1. The application attempts to acquire a lock on the remote state when you first access it
365
- 2. If the backend is locked by a different application, wait and try again
366
- 3. If it succeeds, the lock is held and refreshed periodically
367
- 4. When the application exits, the lock is released
368
- 5. If the application does not refresh its lock, or fails to release it when it exits, the lock will automatically expire within 60 seconds
369
- 6. If another application steals the lock (unlikely but possible), and the application tries to save data, a `StandardError` will be thrown
370
- 7. You can manually attempt to lock/unlock by calling `Rbcli.remote_state.lock` or `Rbcli.remote_state.unlock`, respectively.
371
-
372
- ##### Auto-locking vs Manual Locking
373
-
374
- Remember: all state in Rbcli is lazy-loaded. Therefore, RBCli wll only attempt to lock the data when you first try to access it. If you need to make sure that the data is locked before executing a block of code, use:
156
+ Then it can be accessed when writing your commands via:
375
157
 
376
158
  ```ruby
377
- Rbcli.remote_state.refresh
159
+ Rbcli::log.info { 'These logs can go to STDERR, STDOUT, or a file' }
378
160
  ```
379
161
 
380
- to force the lock and retrieve the latest data. You can force an unlock by calling:
381
-
382
- ```ruby
383
- Rbcli.remote_state.disconnect
384
- ```
162
+ The user will also be able to change the log level and target via their config file, if it is enabled.
385
163
 
386
164
 
387
165
  ## Automatic Update Check
388
166
 
389
- RBCli can automatically notify users when an update is available. If `force_update` is set (see below), RBCli can halt execution until the user updates their application.
390
-
391
- Two sources are currently supported: Github (including Enterprise) and RubyGems.
392
-
393
- ### GitHub Update Check
394
-
395
- The GitHub update check works best when paired with GitHub's best practices on releases. See here: https://help.github.com/articles/creating-releases/
396
-
397
- RBCli will check your github repo's tags and compare that version number with one you configured Rbcli with.
398
-
399
- ```ruby
400
- autoupdate github_repo: 'akhoury6/rbcli', access_token: nil, enterprise_hostname: nil, force_update: false # (Optional) Check for updates to this application at a GitHub repo. The repo must use version number tags in accordance to best practices: https://help.github.com/articles/creating-releases/
401
- ```
402
- The `github_repo` should point to the repo using the `user/repo` syntax.
403
-
404
- The `access_token` can be overridden by the user via their configuration file, so it can be left as `nil`. The token is not needed at all if using a public repo. For instructions on generating a new access token, see [here](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/).
405
-
406
- The `enterprise_hostname` setting allows you to point RBCli at a local GitHub Enterprise server.
407
-
408
- Setting `force_update: true` will halt execution if an update is available, forcing the user to update.
409
-
410
- ### Rubygems.org Update Check
411
-
412
- The Rubygems update check will check if there is a new published version of the gem on Rubygems.org. The latest published version is compared to the version number you configured RBCli with.
413
-
414
- ```ruby
415
- autoupdate gem: 'rbcli', force_update: false # (Optional) Check for updates to this application through RubyGems.org.
416
- ```
417
-
418
- The `gem` parameter should simply state the name of the gem.
419
-
420
- Setting `force_update: true` will halt execution if an update is available, forcing the user to update.
421
-
422
-
423
- ## External Script Wrapping
424
-
425
- RBCli has the ability to run an external application as a CLI command, passing CLI parameters and environment variables as desired. It provides two modes -- __direct path__ and __variable path__ -- which work similarly through the `extern` keyword.
426
-
427
- When an external script is defined in a command, an `action` is no longer required.
428
-
429
- To quickly generate a script that shows the environment variables passed to it, you can use RBCli's own tool: `rbcli script`
430
-
431
- ### Direct Path Mode
432
-
433
- ```ruby
434
- class Test < Rbcli::Command # Declare a new command by subclassing Rbcli::Command
435
- description 'This is a short description.' # (Required) Short description for the global help
436
- usage 'This is some really long usage text description!' # (Required) Long description for the command-specific help
437
- parameter :force, 'Force testing', type: :boolean, default: false, required: false # (Optional, Multiple) Add a command-specific CLI parameter. Can be called multiple times
438
-
439
- extern path: 'env | grep "^__PARAMS\|^__ARGS\|^__GLOBAL\|^__CONFIG\|^MYVAR"', envvars: {MYVAR: 'some_value'} # (Required unless `action` defined) Runs a given application, with optional environment variables, when the user runs the command.
440
- end
441
- ```
167
+ RBCli can automatically notify users when an update is available. Two sources are currently supported: Github (including Enterprise) and RubyGems.
442
168
 
443
- Here, we supply a string to run the command. We can optioanlly provide environment variables which will be available for the script to use.
169
+ You can configure automatic updates in `config/autoupdate.rb` in your project.
444
170
 
445
- RBCli will automatically set several environment variables as well. As you may have guessed by the example above, they are prefixed with:
446
171
 
447
- * `__PARAMS`
448
- * `__ARGS`
449
- * `__GLOBAL`
450
- * `__CONFIG`
451
-
452
- These prefixes are applied to their respective properties in RBCli, similar to what you would see when using an `action`.
453
-
454
- The command in the example above will show you a list of variables, which should look something like this:
455
-
456
- ```bash
457
- __GLOBAL_VERSION=false
458
- __GLOBAL_HELP=false
459
- __GLOBAL_GENERATE_CONFIG=false
460
- __GLOBAL_CONFIG_FILE="/etc/mytool/config.yml"
461
- __CONFIG_LOGGER={"log_level":"info","log_target":"stderr"}
462
- __CONFIG_MYOPT=true
463
- __CONFIG_GITHUB_UPDATE={"access_token":null,"enterprise_hostname":null}
464
- __PARAMS_FORCE=false
465
- __PARAMS_HELP=false
466
- MYVAR=some_value
467
- ```
468
-
469
- As you can see above, items which have nested values they are passed in as JSON. If you need to parse them, [JQ](https://stedolan.github.io/jq/) is recommended.
470
-
471
- ### Variable Path Mode
472
-
473
- Variable Path Mode works the same as Direct Path Mode, only instead of providing a string we provide a block that returns a string. This allows us to generate different commands based on the CLI parameters that the user passed, or pass configuration as CLI parameters to the external application:
474
-
475
- ```ruby
476
- class Test < Rbcli::Command # Declare a new command by subclassing Rbcli::Command
477
- description 'This is a short description.' # (Required) Short description for the global help
478
- usage 'This is some really long usage text description!' # (Required) Long description for the command-specific help
479
- parameter :force, 'Force testing', type: :boolean, default: false, required: false # (Optional, Multiple) Add a command-specific CLI parameter. Can be called multiple times
480
-
481
- 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.
482
- if params[:force]
483
- "externalapp --test-script foo --ignore-errors"
484
- else
485
- "externalapp"
486
- end
487
- end
488
- end
489
- ```
490
-
491
- ## Project Structure and Generators
492
-
493
- RBCli supports using predefined project structure, helping to organize all of the options and commands that you may use. It also
494
-
495
- 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.
496
-
497
- The folder structure is as follows:
498
-
499
- ```
500
- <projectname>
501
- |
502
- |--- application
503
- | |
504
- | |--- commands
505
- | |
506
- | |---scripts
507
- |
508
- |--- config
509
- |--- default_user_configs
510
- |--- exe
511
- |--- hooks
512
- |--- spec
513
- ```
514
-
515
- It is highly recommended to __not__ create files in these folders manually, and to use the RBCli generators instead:
516
-
517
- ```shell
518
- rbcli command -n <name>
519
- rbcli script -n <name>
520
- rbcli userconf -n <name>
521
- rbcli hook --default # or rbcli hook -d
522
- rbcli hook --pre # or rbcli hook -p
523
- rbcli hook --post # or rbcli hook -o
524
- rbcli hook --firstrun # or rbcli hook -f
525
- rbcli hook -dpof # all hooks at once
526
- ```
527
-
528
- That said, this readme will provide you with the information required to do things manually if you so desire. More details on generators later.
529
-
530
-
531
- ## Development
532
-
533
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
534
-
535
- To install this gem onto your local machine from source, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
536
-
537
-
538
- ## Contributing
539
-
540
- Bug reports and pull requests are welcome on GitHub at https://github.com/akhoury6/rbcli. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
172
+ ## Development and Contributing
541
173
 
174
+ For more information about development and contributing, please see the [Official Development Documentation][documentation_development]
542
175
 
543
176
  ## License
544
177
 
545
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
178
+ The gem is available as open source under the terms of the [GPLv3](https://github.com/akhoury6/rbcli/blob/master/CODE_OF_CONDUCT.md).
179
+
180
+ ## Full Documentation
546
181
 
182
+ [You can find the Official Documentation for RBCli Here.][documentation_home]
547
183
 
548
- ## Code of Conduct
549
184
 
550
- Everyone interacting in the Rbcli project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/akhoury6/rbcli/blob/master/CODE_OF_CONDUCT.md).
185
+ [documentation_home]: https://akhoury6.github.com/rbcli
186
+ [documentation_development]: https://akhoury6.github.com/rbcli/development/contributing/
187
+ [license_text]: https://github.com/akhoury6/rbcli/blob/master/LICENSE.txt
data/bin/console CHANGED
@@ -1,4 +1,23 @@
1
1
  #!/usr/bin/env ruby
2
+ ##################################################################################
3
+ # RBCli -- A framework for developing command line applications in Ruby #
4
+ # Copyright (C) 2018 Andrew Khoury #
5
+ # #
6
+ # This program is free software: you can redistribute it and/or modify #
7
+ # it under the terms of the GNU General Public License as published by #
8
+ # the Free Software Foundation, either version 3 of the License, or #
9
+ # (at your option) any later version. #
10
+ # #
11
+ # This program is distributed in the hope that it will be useful, #
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14
+ # GNU General Public License for more details. #
15
+ # #
16
+ # You should have received a copy of the GNU General Public License #
17
+ # along with this program. If not, see <https://www.gnu.org/licenses/>. #
18
+ # #
19
+ # For questions regarding licensing, please contact andrew@blacknex.us #
20
+ ##################################################################################
2
21
 
3
22
  require "bundler/setup"
4
23
  require "rbcli"