toys 0.12.1 → 0.13.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +44 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +7 -4
  6. data/builtins/system/git-cache.rb +238 -0
  7. data/builtins/system/test.rb +37 -2
  8. data/core-docs/toys/acceptor.rb +432 -0
  9. data/core-docs/toys/arg_parser.rb +397 -0
  10. data/core-docs/toys/cli.rb +493 -0
  11. data/core-docs/toys/compat.rb +2 -0
  12. data/core-docs/toys/completion.rb +329 -0
  13. data/core-docs/toys/context.rb +321 -0
  14. data/core-docs/toys/core.rb +14 -0
  15. data/core-docs/toys/dsl/base.rb +56 -0
  16. data/core-docs/toys/dsl/flag.rb +261 -0
  17. data/core-docs/toys/dsl/flag_group.rb +259 -0
  18. data/core-docs/toys/dsl/internal.rb +4 -0
  19. data/core-docs/toys/dsl/positional_arg.rb +139 -0
  20. data/core-docs/toys/dsl/tool.rb +1530 -0
  21. data/core-docs/toys/errors.rb +93 -0
  22. data/core-docs/toys/flag.rb +549 -0
  23. data/core-docs/toys/flag_group.rb +186 -0
  24. data/core-docs/toys/input_file.rb +8 -0
  25. data/core-docs/toys/loader.rb +222 -0
  26. data/core-docs/toys/middleware.rb +295 -0
  27. data/core-docs/toys/mixin.rb +142 -0
  28. data/core-docs/toys/module_lookup.rb +75 -0
  29. data/core-docs/toys/positional_arg.rb +145 -0
  30. data/core-docs/toys/settings.rb +507 -0
  31. data/core-docs/toys/source_info.rb +127 -0
  32. data/core-docs/toys/standard_middleware/add_verbosity_flags.rb +49 -0
  33. data/core-docs/toys/standard_middleware/apply_config.rb +24 -0
  34. data/core-docs/toys/standard_middleware/handle_usage_errors.rb +33 -0
  35. data/core-docs/toys/standard_middleware/set_default_descriptions.rb +222 -0
  36. data/core-docs/toys/standard_middleware/show_help.rb +190 -0
  37. data/core-docs/toys/standard_middleware/show_root_version.rb +45 -0
  38. data/core-docs/toys/standard_mixins/bundler.rb +83 -0
  39. data/core-docs/toys/standard_mixins/exec.rb +645 -0
  40. data/core-docs/toys/standard_mixins/fileutils.rb +18 -0
  41. data/core-docs/toys/standard_mixins/gems.rb +48 -0
  42. data/core-docs/toys/standard_mixins/git_cache.rb +41 -0
  43. data/core-docs/toys/standard_mixins/highline.rb +133 -0
  44. data/core-docs/toys/standard_mixins/terminal.rb +135 -0
  45. data/core-docs/toys/standard_mixins/xdg.rb +49 -0
  46. data/core-docs/toys/template.rb +112 -0
  47. data/core-docs/toys/tool_definition.rb +926 -0
  48. data/core-docs/toys/utils/completion_engine.rb +49 -0
  49. data/core-docs/toys/utils/exec.rb +721 -0
  50. data/core-docs/toys/utils/gems.rb +185 -0
  51. data/core-docs/toys/utils/git_cache.rb +353 -0
  52. data/core-docs/toys/utils/help_text.rb +134 -0
  53. data/core-docs/toys/utils/terminal.rb +310 -0
  54. data/core-docs/toys/utils/xdg.rb +253 -0
  55. data/core-docs/toys/wrappable_string.rb +120 -0
  56. data/core-docs/toys-core.rb +63 -0
  57. data/docs/guide.md +497 -156
  58. data/lib/toys/standard_cli.rb +50 -36
  59. data/lib/toys/templates/clean.rb +18 -0
  60. data/lib/toys/templates/gem_build.rb +24 -0
  61. data/lib/toys/templates/minitest.rb +21 -0
  62. data/lib/toys/templates/rake.rb +23 -3
  63. data/lib/toys/templates/rdoc.rb +29 -0
  64. data/lib/toys/templates/rspec.rb +32 -4
  65. data/lib/toys/templates/rubocop.rb +14 -1
  66. data/lib/toys/templates/yardoc.rb +55 -0
  67. data/lib/toys/testing.rb +186 -109
  68. data/lib/toys/version.rb +1 -1
  69. data/lib/toys.rb +4 -2
  70. metadata +56 -6
@@ -93,10 +93,16 @@ module Toys
93
93
  # Create a standard CLI, configured with the appropriate paths and
94
94
  # middleware.
95
95
  #
96
+ # @param custom_paths [String,Array<String>] Custom paths to use. If set,
97
+ # the CLI uses only the given paths. If not, the CLI will search for
98
+ # paths from the current directory and global paths.
99
+ # @param include_builtins [boolean] Add the builtin tools. Default is true.
96
100
  # @param cur_dir [String,nil] Starting search directory for configs.
97
101
  # Defaults to the current working directory.
98
102
  #
99
- def initialize(cur_dir: nil)
103
+ def initialize(custom_paths: nil,
104
+ include_builtins: true,
105
+ cur_dir: nil)
100
106
  super(
101
107
  executable_name: EXECUTABLE_NAME,
102
108
  config_dir_name: CONFIG_DIR_NAME,
@@ -110,40 +116,48 @@ module Toys
110
116
  middleware_stack: default_middleware_stack,
111
117
  template_lookup: default_template_lookup
112
118
  )
113
- add_standard_paths(cur_dir: cur_dir, toys_dir_name: CONFIG_DIR_NAME)
119
+ if custom_paths
120
+ Array(custom_paths).each { |path| add_config_path(path) }
121
+ else
122
+ add_current_directory_paths(cur_dir)
123
+ end
124
+ add_builtins if include_builtins
114
125
  end
115
126
 
116
127
  private
117
128
 
118
129
  ##
119
- # Add paths for a toys standard CLI. Paths added, in order from high to
120
- # low priority, are:
130
+ # Add paths for builtin tools
121
131
  #
122
- # * Search the current directory and all ancestors for config files and
123
- # directories.
124
- # * Read the `TOYS_PATH` environment variable and search for config files
125
- # and directories in the given paths. If this variable is empty, use
126
- # `$HOME:/etc` by default.
127
- # * The builtins for the standard toys executable.
132
+ def add_builtins
133
+ builtins_path = ::File.join(::File.dirname(::File.dirname(__dir__)), "builtins")
134
+ add_config_path(builtins_path, source_name: "(builtin tools)", context_directory: nil)
135
+ self
136
+ end
137
+
138
+ ##
139
+ # Add paths for the given current directory and its ancestors, plus the
140
+ # global paths.
128
141
  #
129
- # @param cur_dir [String,nil] Starting search directory for configs.
130
- # Defaults to the current working directory.
131
- # @param global_dirs [Array<String>,nil] Optional list of global
132
- # directories, or `nil` to use the defaults.
142
+ # @param cur_dir [String] The starting directory path, or nil to use the
143
+ # current directory
133
144
  # @return [self]
134
145
  #
135
- def add_standard_paths(cur_dir: nil, global_dirs: nil, toys_dir_name: nil)
136
- cur_dir ||= ::Dir.pwd
137
- cur_dir = skip_toys_dir(cur_dir, toys_dir_name) if toys_dir_name
138
- global_dirs ||= default_global_dirs
146
+ def add_current_directory_paths(cur_dir)
147
+ cur_dir = skip_toys_dir(cur_dir || ::Dir.pwd, CONFIG_DIR_NAME)
148
+ global_dirs = default_global_dirs
139
149
  add_search_path_hierarchy(start: cur_dir, terminate: global_dirs)
140
150
  global_dirs.each { |path| add_search_path(path) }
141
- builtins_path = ::File.join(::File.dirname(::File.dirname(__dir__)), "builtins")
142
- add_config_path(builtins_path, source_name: "(builtin tools)", context_directory: nil)
143
151
  self
144
152
  end
145
153
 
146
- # Step out of any toys dir
154
+ ##
155
+ # Step out of any toys dir.
156
+ #
157
+ # @param dir [String] The starting path
158
+ # @param toys_dir_name [String] The name of the toys directory to look for
159
+ # @return [String] The final directory path
160
+ #
147
161
  def skip_toys_dir(dir, toys_dir_name)
148
162
  cur_dir = dir
149
163
  loop do
@@ -157,6 +171,21 @@ module Toys
157
171
  end
158
172
  end
159
173
 
174
+ ##
175
+ # Returns the default set of global config directories.
176
+ #
177
+ # @return [Array<String>]
178
+ #
179
+ def default_global_dirs
180
+ paths = ::ENV[TOYS_PATH_ENV].to_s.split(::File::PATH_SEPARATOR)
181
+ paths = [::Dir.home, "/etc"] if paths.empty?
182
+ paths
183
+ .compact
184
+ .uniq
185
+ .select { |path| ::File.directory?(path) && ::File.readable?(path) }
186
+ .map { |path| ::File.realpath(::File.expand_path(path)) }
187
+ end
188
+
160
189
  ##
161
190
  # Returns the middleware for the standard Toys CLI.
162
191
  #
@@ -188,21 +217,6 @@ module Toys
188
217
  ]
189
218
  end
190
219
 
191
- ##
192
- # Returns the default set of global config directories.
193
- #
194
- # @return [Array<String>]
195
- #
196
- def default_global_dirs
197
- paths = ::ENV[TOYS_PATH_ENV].to_s.split(::File::PATH_SEPARATOR)
198
- paths = [::Dir.home, "/etc"] if paths.empty?
199
- paths
200
- .compact
201
- .uniq
202
- .select { |path| ::File.directory?(path) && ::File.readable?(path) }
203
- .map { |path| ::File.realpath(::File.expand_path(path)) }
204
- end
205
-
206
220
  ##
207
221
  # Returns a ModuleLookup for the default templates.
208
222
  #
@@ -56,15 +56,21 @@ module Toys
56
56
  #
57
57
  attr_writer :context_directory
58
58
 
59
+ ##
59
60
  # @private
61
+ #
60
62
  attr_reader :context_directory
61
63
 
64
+ ##
62
65
  # @private
66
+ #
63
67
  def paths
64
68
  Array(@paths)
65
69
  end
66
70
 
71
+ ##
67
72
  # @private
73
+ #
68
74
  def name
69
75
  @name || DEFAULT_TOOL_NAME
70
76
  end
@@ -80,7 +86,9 @@ module Toys
80
86
  include :fileutils
81
87
  include :exec
82
88
 
89
+ ##
83
90
  # @private
91
+ #
84
92
  def run
85
93
  cd(context_directory || ::Dir.getwd) do
86
94
  template_paths.each do |elem|
@@ -96,7 +104,9 @@ module Toys
96
104
  end
97
105
  end
98
106
 
107
+ ##
99
108
  # @private
109
+ #
100
110
  def clean_gitignore
101
111
  result = exec(["git", "rev-parse", "--is-inside-work-tree"], out: :null, err: :null)
102
112
  unless result.success?
@@ -106,7 +116,9 @@ module Toys
106
116
  clean_gitignore_dir(".")
107
117
  end
108
118
 
119
+ ##
109
120
  # @private
121
+ #
110
122
  def clean_gitignore_dir(dir)
111
123
  children = dir_children(dir)
112
124
  result = exec(["git", "check-ignore", "--stdin"],
@@ -118,7 +130,9 @@ module Toys
118
130
  children.each { |child| clean_gitignore_dir(child) if ::File.directory?(child) }
119
131
  end
120
132
 
133
+ ##
121
134
  # @private
135
+ #
122
136
  def dir_children(dir)
123
137
  ::Dir.entries(dir)
124
138
  .reject { |entry| entry =~ /^\.\.?$/ }
@@ -126,12 +140,16 @@ module Toys
126
140
  .map { |entry| ::File.join(dir, entry) }
127
141
  end
128
142
 
143
+ ##
129
144
  # @private
145
+ #
130
146
  def clean_pattern(pattern)
131
147
  ::Dir.glob(pattern) { |path| clean_path(path) }
132
148
  end
133
149
 
150
+ ##
134
151
  # @private
152
+ #
135
153
  def clean_path(path)
136
154
  if ::File.exist?(path)
137
155
  rm_rf(path)
@@ -149,23 +149,41 @@ module Toys
149
149
  #
150
150
  attr_writer :context_directory
151
151
 
152
+ ##
152
153
  # @private
154
+ #
153
155
  attr_reader :output
156
+
157
+ ##
154
158
  # @private
159
+ #
155
160
  attr_reader :push_gem
161
+
162
+ ##
156
163
  # @private
164
+ #
157
165
  attr_reader :install_gem
166
+
167
+ ##
158
168
  # @private
169
+ #
159
170
  attr_reader :tag
171
+
172
+ ##
160
173
  # @private
174
+ #
161
175
  attr_reader :context_directory
162
176
 
177
+ ##
163
178
  # @private
179
+ #
164
180
  def name
165
181
  @name || DEFAULT_TOOL_NAME
166
182
  end
167
183
 
184
+ ##
168
185
  # @private
186
+ #
169
187
  def gem_name(context_dir = nil)
170
188
  return @gem_name if @gem_name
171
189
  glob = "*.gemspec"
@@ -177,17 +195,23 @@ module Toys
177
195
  candidates.first.sub(/\.gemspec$/, "")
178
196
  end
179
197
 
198
+ ##
180
199
  # @private
200
+ #
181
201
  def output_flags
182
202
  @output_flags == true ? DEFAULT_OUTPUT_FLAGS : Array(@output_flags)
183
203
  end
184
204
 
205
+ ##
185
206
  # @private
207
+ #
186
208
  def push_tag
187
209
  @push_tag == true ? DEFAULT_PUSH_REMOTE : @push_tag
188
210
  end
189
211
 
212
+ ##
190
213
  # @private
214
+ #
191
215
  def task_names
192
216
  names = []
193
217
  names << "Install" if @install_gem
@@ -175,37 +175,58 @@ module Toys
175
175
  self
176
176
  end
177
177
 
178
+ ##
178
179
  # @private
180
+ #
179
181
  attr_reader :seed
182
+
183
+ ##
180
184
  # @private
185
+ #
181
186
  attr_reader :verbose
187
+
188
+ ##
182
189
  # @private
190
+ #
183
191
  attr_reader :warnings
192
+
193
+ ##
184
194
  # @private
195
+ #
185
196
  attr_reader :context_directory
186
197
 
198
+ ##
187
199
  # @private
200
+ #
188
201
  def name
189
202
  @name || DEFAULT_TOOL_NAME
190
203
  end
191
204
 
205
+ ##
192
206
  # @private
207
+ #
193
208
  def libs
194
209
  @libs ? Array(@libs) : DEFAULT_LIBS
195
210
  end
196
211
 
212
+ ##
197
213
  # @private
214
+ #
198
215
  def files
199
216
  @files ? Array(@files) : DEFAULT_FILES
200
217
  end
201
218
 
219
+ ##
202
220
  # @private
221
+ #
203
222
  def gem_version
204
223
  return Array(@gem_version) if @gem_version
205
224
  @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS
206
225
  end
207
226
 
227
+ ##
208
228
  # @private
229
+ #
209
230
  def bundler_settings
210
231
  if @bundler && !@bundler.is_a?(::Hash)
211
232
  {}
@@ -120,24 +120,38 @@ module Toys
120
120
  self
121
121
  end
122
122
 
123
+ ##
123
124
  # @private
125
+ #
124
126
  attr_reader :only_described
127
+
128
+ ##
125
129
  # @private
130
+ #
126
131
  attr_reader :use_flags
132
+
133
+ ##
127
134
  # @private
135
+ #
128
136
  attr_reader :context_directory
129
137
 
138
+ ##
130
139
  # @private
140
+ #
131
141
  def gem_version
132
142
  Array(@gem_version)
133
143
  end
134
144
 
145
+ ##
135
146
  # @private
147
+ #
136
148
  def rakefile_path
137
149
  @rakefile_path || DEFAULT_RAKEFILE_PATH
138
150
  end
139
151
 
152
+ ##
140
153
  # @private
154
+ #
141
155
  def bundler_settings
142
156
  if @bundler && !@bundler.is_a?(::Hash)
143
157
  {}
@@ -191,7 +205,9 @@ module Toys
191
205
  end
192
206
  end
193
207
 
194
- ## @private
208
+ ##
209
+ # @private
210
+ #
195
211
  def self.flag_specs(arg)
196
212
  name = arg.to_s.gsub(/\W/, "").downcase
197
213
  specs = []
@@ -203,7 +219,9 @@ module Toys
203
219
  specs
204
220
  end
205
221
 
206
- ## @private
222
+ ##
223
+ # @private
224
+ #
207
225
  def self.find_rakefile(path, context_dir)
208
226
  if path == ::File.absolute_path(path)
209
227
  return ::File.file?(path) && ::File.readable?(path) ? path : nil
@@ -220,7 +238,9 @@ module Toys
220
238
  nil
221
239
  end
222
240
 
223
- ## @private
241
+ ##
242
+ # @private
243
+ #
224
244
  def self.prepare_rake(rakefile_path, context_dir)
225
245
  ::Rake::TaskManager.record_task_metadata = true
226
246
  rake = ::Rake::Application.new
@@ -214,46 +214,75 @@ module Toys
214
214
  self
215
215
  end
216
216
 
217
+ ##
217
218
  # @private
219
+ #
218
220
  attr_reader :markup
221
+
222
+ ##
219
223
  # @private
224
+ #
220
225
  attr_reader :title
226
+
227
+ ##
221
228
  # @private
229
+ #
222
230
  attr_reader :main
231
+
232
+ ##
223
233
  # @private
234
+ #
224
235
  attr_reader :template
236
+
237
+ ##
225
238
  # @private
239
+ #
226
240
  attr_reader :generator
241
+
242
+ ##
227
243
  # @private
244
+ #
228
245
  attr_reader :context_directory
229
246
 
247
+ ##
230
248
  # @private
249
+ #
231
250
  def name
232
251
  @name || DEFAULT_TOOL_NAME
233
252
  end
234
253
 
254
+ ##
235
255
  # @private
256
+ #
236
257
  def gem_version
237
258
  return Array(@gem_version) if @gem_version
238
259
  @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS
239
260
  end
240
261
 
262
+ ##
241
263
  # @private
264
+ #
242
265
  def files
243
266
  @files ? Array(@files) : DEFAULT_FILES
244
267
  end
245
268
 
269
+ ##
246
270
  # @private
271
+ #
247
272
  def output_dir
248
273
  @output_dir || DEFAULT_OUTPUT_DIR
249
274
  end
250
275
 
276
+ ##
251
277
  # @private
278
+ #
252
279
  def options
253
280
  Array(@options)
254
281
  end
255
282
 
283
+ ##
256
284
  # @private
285
+ #
257
286
  def bundler_settings
258
287
  if @bundler && !@bundler.is_a?(::Hash)
259
288
  {}
@@ -221,49 +221,77 @@ module Toys
221
221
  self
222
222
  end
223
223
 
224
- ## @private
224
+ ##
225
+ # @private
226
+ #
225
227
  attr_reader :options
226
- ## @private
228
+
229
+ ##
230
+ # @private
231
+ #
227
232
  attr_reader :out
228
- ## @private
233
+
234
+ ##
235
+ # @private
236
+ #
229
237
  attr_reader :backtrace
230
- ## @private
238
+
239
+ ##
240
+ # @private
241
+ #
231
242
  attr_reader :warnings
243
+
244
+ ##
232
245
  # @private
246
+ #
233
247
  attr_reader :context_directory
234
248
 
249
+ ##
235
250
  # @private
251
+ #
236
252
  def name
237
253
  @name || DEFAULT_TOOL_NAME
238
254
  end
239
255
 
256
+ ##
240
257
  # @private
258
+ #
241
259
  def gem_version
242
260
  return Array(@gem_version) if @gem_version
243
261
  @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS
244
262
  end
245
263
 
264
+ ##
246
265
  # @private
266
+ #
247
267
  def libs
248
268
  @libs ? Array(@libs) : DEFAULT_LIBS
249
269
  end
250
270
 
271
+ ##
251
272
  # @private
273
+ #
252
274
  def order
253
275
  @order || DEFAULT_ORDER
254
276
  end
255
277
 
278
+ ##
256
279
  # @private
280
+ #
257
281
  def format
258
282
  @format || DEFAULT_FORMAT
259
283
  end
260
284
 
285
+ ##
261
286
  # @private
287
+ #
262
288
  def pattern
263
289
  @pattern || DEFAULT_PATTERN
264
290
  end
265
291
 
292
+ ##
266
293
  # @private
294
+ #
267
295
  def bundler_settings
268
296
  if @bundler && !@bundler.is_a?(::Hash)
269
297
  {}
@@ -124,28 +124,41 @@ module Toys
124
124
  self
125
125
  end
126
126
 
127
- ## @private
127
+ ##
128
+ # @private
129
+ #
128
130
  attr_reader :fail_on_error
131
+
132
+ ##
129
133
  # @private
134
+ #
130
135
  attr_reader :context_directory
131
136
 
137
+ ##
132
138
  # @private
139
+ #
133
140
  def name
134
141
  @name || DEFAULT_TOOL_NAME
135
142
  end
136
143
 
144
+ ##
137
145
  # @private
146
+ #
138
147
  def gem_version
139
148
  return Array(@gem_version) if @gem_version
140
149
  @bundler ? [] : DEFAULT_GEM_VERSION_REQUIREMENTS
141
150
  end
142
151
 
152
+ ##
143
153
  # @private
154
+ #
144
155
  def options
145
156
  Array(@options)
146
157
  end
147
158
 
159
+ ##
148
160
  # @private
161
+ #
149
162
  def bundler_settings
150
163
  if @bundler && !@bundler.is_a?(::Hash)
151
164
  {}