toys 0.12.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +35 -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
  {}