toys-core 0.11.5 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +1 -1
  4. data/lib/toys-core.rb +4 -1
  5. data/lib/toys/acceptor.rb +3 -3
  6. data/lib/toys/arg_parser.rb +6 -7
  7. data/lib/toys/cli.rb +44 -14
  8. data/lib/toys/compat.rb +19 -22
  9. data/lib/toys/completion.rb +3 -1
  10. data/lib/toys/context.rb +2 -2
  11. data/lib/toys/core.rb +1 -1
  12. data/lib/toys/dsl/base.rb +85 -0
  13. data/lib/toys/dsl/flag.rb +3 -3
  14. data/lib/toys/dsl/flag_group.rb +7 -7
  15. data/lib/toys/dsl/internal.rb +206 -0
  16. data/lib/toys/dsl/positional_arg.rb +3 -3
  17. data/lib/toys/dsl/tool.rb +174 -216
  18. data/lib/toys/errors.rb +1 -0
  19. data/lib/toys/flag.rb +15 -18
  20. data/lib/toys/flag_group.rb +5 -4
  21. data/lib/toys/input_file.rb +4 -4
  22. data/lib/toys/loader.rb +189 -50
  23. data/lib/toys/middleware.rb +1 -1
  24. data/lib/toys/mixin.rb +2 -2
  25. data/lib/toys/positional_arg.rb +3 -3
  26. data/lib/toys/settings.rb +900 -0
  27. data/lib/toys/source_info.rb +121 -18
  28. data/lib/toys/standard_middleware/apply_config.rb +5 -4
  29. data/lib/toys/standard_middleware/set_default_descriptions.rb +18 -18
  30. data/lib/toys/standard_middleware/show_help.rb +17 -5
  31. data/lib/toys/standard_mixins/exec.rb +12 -14
  32. data/lib/toys/standard_mixins/git_cache.rb +48 -0
  33. data/lib/toys/standard_mixins/xdg.rb +56 -0
  34. data/lib/toys/template.rb +2 -2
  35. data/lib/toys/{tool.rb → tool_definition.rb} +100 -41
  36. data/lib/toys/utils/exec.rb +4 -5
  37. data/lib/toys/utils/gems.rb +8 -7
  38. data/lib/toys/utils/git_cache.rb +184 -0
  39. data/lib/toys/utils/help_text.rb +90 -34
  40. data/lib/toys/utils/terminal.rb +1 -1
  41. data/lib/toys/utils/xdg.rb +293 -0
  42. metadata +14 -7
@@ -14,7 +14,7 @@ module Toys
14
14
  ##
15
15
  # A simple terminal class.
16
16
  #
17
- # ## Styles
17
+ # ### Styles
18
18
  #
19
19
  # This class supports ANSI styled output where supported.
20
20
  #
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ module Utils
5
+ ##
6
+ # A class that provides tools for working with the XDG Base Directory
7
+ # Specification.
8
+ #
9
+ # This class provides utility methods that locate base directories and
10
+ # search paths for application state, configuration, caches, and other
11
+ # data, according to the [XDG Base Directory Spec version
12
+ # 0.8](https://specifications.freedesktop.org/basedir-spec/0.8/).
13
+ #
14
+ # Tools can use the `:xdg` mixin for convenient access to this class.
15
+ #
16
+ # ### Example
17
+ #
18
+ # require "toys/utils/xdg"
19
+ #
20
+ # xdg = Toys::Utils::XDG.new
21
+ #
22
+ # # Get config file paths, in order from most to least inportant
23
+ # config_files = xdg.lookup_config("my-config.toml")
24
+ # config_files.each { |path| read_my_config(path) }
25
+ #
26
+ # ### Windows operation
27
+ #
28
+ # The Spec assumes a unix-like environment, and cannot be applied directly
29
+ # to Windows without modification. In general, this class will function on
30
+ # Windows, but with the following caveats:
31
+ #
32
+ # * All file paths must use Windows-style absolute paths, beginning with
33
+ # the drive letter.
34
+ # * Environment variables that can contain multiple paths (`XDG_*_DIRS`)
35
+ # use the Windows path delimiter (`;`) rather than the unix path
36
+ # delimiter (`:`).
37
+ # * Defaults for home directories (`XDG_*_HOME`) will follow unix
38
+ # conventions, using subdirectories under the user's profile directory
39
+ # rather than the Windows known folder paths.
40
+ # * Defaults for search paths (`XDG_*_DIRS`) will be empty and will not
41
+ # use the Windows known folder paths.
42
+ #
43
+ class XDG
44
+ ##
45
+ # Create an instance of XDG.
46
+ #
47
+ # @param env [Hash{String=>String}] the environment variables. Normally,
48
+ # you can omit this argument, as it will default to `::ENV`.
49
+ #
50
+ def initialize(env: ::ENV)
51
+ @env = env
52
+ end
53
+
54
+ ##
55
+ # Returns the absolute path to the current user's home directory.
56
+ #
57
+ # @return [String]
58
+ #
59
+ def home_dir
60
+ @home_dir ||= validate_dir_env("HOME") || ::Dir.home
61
+ end
62
+
63
+ ##
64
+ # Returns the absolute path to the single base directory relative to
65
+ # which user-specific data files should be written.
66
+ # Corresponds to the value of the `$XDG_DATA_HOME` environment variable
67
+ # and its defaults according to the XDG Base Directory Spec.
68
+ #
69
+ # @return [String]
70
+ #
71
+ def data_home
72
+ @data_home ||=
73
+ validate_dir_env("XDG_DATA_HOME") || ::File.join(home_dir, ".local", "share")
74
+ end
75
+
76
+ ##
77
+ # Returns the absolute path to the single base directory relative to
78
+ # which user-specific configuration files should be written.
79
+ # Corresponds to the value of the `$XDG_CONFIG_HOME` environment variable
80
+ # and its defaults according to the XDG Base Directory Spec.
81
+ #
82
+ # @return [String]
83
+ #
84
+ def config_home
85
+ @config_home ||= validate_dir_env("XDG_CONFIG_HOME") || ::File.join(home_dir, ".config")
86
+ end
87
+
88
+ ##
89
+ # Returns the absolute path to the single base directory relative to
90
+ # which user-specific state files should be written.
91
+ # Corresponds to the value of the `$XDG_STATE_HOME` environment variable
92
+ # and its defaults according to the XDG Base Directory Spec.
93
+ #
94
+ # @return [String]
95
+ #
96
+ def state_home
97
+ @state_home ||=
98
+ validate_dir_env("XDG_STATE_HOME") || ::File.join(home_dir, ".local", "state")
99
+ end
100
+
101
+ ##
102
+ # Returns the absolute path to the single base directory relative to
103
+ # which user-specific non-essential (cached) data should be written.
104
+ # Corresponds to the value of the `$XDG_CACHE_HOME` environment variable
105
+ # and its defaults according to the XDG Base Directory Spec.
106
+ #
107
+ # @return [String]
108
+ #
109
+ def cache_home
110
+ @cache_home ||= validate_dir_env("XDG_CACHE_HOME") || ::File.join(home_dir, ".cache")
111
+ end
112
+
113
+ ##
114
+ # Returns the absolute path to the single base directory relative to
115
+ # which user-specific executable files may be written.
116
+ # Returns the value of `$HOME/.local/bin` as specified by the XDG Base
117
+ # Directory Spec.
118
+ #
119
+ # @return [String]
120
+ #
121
+ def executable_home
122
+ @executable_home ||= ::File.join(home_dir, ".local", "bin")
123
+ end
124
+
125
+ ##
126
+ # Returns the set of preference ordered base directories relative to
127
+ # which data files should be searched, as an array of absolute paths.
128
+ # The array is ordered from most to least important, and does _not_
129
+ # include the data home directory.
130
+ # Corresponds to the value of the `$XDG_DATA_DIRS` environment variable
131
+ # and its defaults according to the XDG Base Directory Spec.
132
+ #
133
+ # @return [Array<String>]
134
+ #
135
+ def data_dirs
136
+ @data_dirs ||= validate_dirs_env("XDG_DATA_DIRS") ||
137
+ validate_dirs(["/usr/local/share", "/usr/share"]) || []
138
+ end
139
+
140
+ ##
141
+ # Returns the set of preference ordered base directories relative to
142
+ # which configuration files should be searched, as an array of absolute
143
+ # paths. The array is ordered from most to least important, and does
144
+ # _not_ include the config home directory.
145
+ # Corresponds to the value of the `$XDG_CONFIG_DIRS` environment variable
146
+ # and its defaults according to the XDG Base Directory Spec.
147
+ #
148
+ # @return [Array<String>]
149
+ #
150
+ def config_dirs
151
+ @config_dirs ||= validate_dirs_env("XDG_CONFIG_DIRS") ||
152
+ validate_dirs(["/etc/xdg"]) || []
153
+ end
154
+
155
+ ##
156
+ # Returns the absolute path to the single base directory relative to
157
+ # which user-specific runtime files and other file objects should be
158
+ # placed. May return `nil` if no such directory could be determined.
159
+ #
160
+ # @return [String,nil]
161
+ #
162
+ def runtime_dir
163
+ @runtime_dir = validate_dir_env("XDG_RUNTIME_DIR") unless defined? @runtime_dir
164
+ @runtime_dir
165
+ end
166
+
167
+ ##
168
+ # Searches the data directories for an object with the given relative
169
+ # path, and returns an array of absolute paths to all objects found in
170
+ # the data directories (i.e. in {#data_dirs} or {#data_home}), in order
171
+ # from most to least important.
172
+ #
173
+ # @param path [String] Relative path of the object to search for
174
+ # @param type [String,Symbol,Array<String,Symbol>] The type(s) of objects
175
+ # to find. You can specify any of the types defined by
176
+ # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype),
177
+ # such as `file` or `directory`, or the special type `any`. Types can
178
+ # be specified as strings or the corresponding symbols. If this
179
+ # argument is not provided, the default of `file` is used.
180
+ # @return [Array<String>]
181
+ #
182
+ def lookup_data(path, type: :file)
183
+ lookup_internal([data_home] + data_dirs, path, type)
184
+ end
185
+
186
+ ##
187
+ # Searches the config directories for an object with the given relative
188
+ # path, and returns an array of absolute paths to all objects found in
189
+ # the config directories (i.e. in {#config_dirs} or {#config_home}), in
190
+ # order from most to least important.
191
+ #
192
+ # @param path [String] Relative path of the object to search for
193
+ # @param type [String,Symbol,Array<String,Symbol>] The type(s) of objects
194
+ # to find. You can specify any of the types defined by
195
+ # [File::Stat#ftype](https://ruby-doc.org/core/File/Stat.html#method-i-ftype),
196
+ # such as `file` or `directory`, or the special type `any`. Types can
197
+ # be specified as strings or the corresponding symbols. If this
198
+ # argument is not provided, the default of `file` is used.
199
+ # @return [Array<String>]
200
+ #
201
+ def lookup_config(path, type: :file)
202
+ lookup_internal([config_home] + config_dirs, path, type)
203
+ end
204
+
205
+ ##
206
+ # Returns the absolute path to a directory under {#data_home}, creating
207
+ # it if it doesn't already exist.
208
+ #
209
+ # @param path [String] The relative path to the subdir within the base
210
+ # data directory.
211
+ # @return [String] The absolute path to the subdir.
212
+ # @raise [Errno::EEXIST] If a non-directory already exists there
213
+ #
214
+ def ensure_data_subdir(path)
215
+ ensure_subdir_internal(data_home, path)
216
+ end
217
+
218
+ ##
219
+ # Returns the absolute path to a directory under {#config_home}, creating
220
+ # it if it doesn't already exist.
221
+ #
222
+ # @param path [String] The relative path to the subdir within the base
223
+ # config directory.
224
+ # @return [String] The absolute path to the subdir.
225
+ # @raise [Errno::EEXIST] If a non-directory already exists there
226
+ #
227
+ def ensure_config_subdir(path)
228
+ ensure_subdir_internal(config_home, path)
229
+ end
230
+
231
+ ##
232
+ # Returns the absolute path to a directory under {#state_home}, creating
233
+ # it if it doesn't already exist.
234
+ #
235
+ # @param path [String] The relative path to the subdir within the base
236
+ # state directory.
237
+ # @return [String] The absolute path to the subdir.
238
+ # @raise [Errno::EEXIST] If a non-directory already exists there
239
+ #
240
+ def ensure_state_subdir(path)
241
+ ensure_subdir_internal(state_home, path)
242
+ end
243
+
244
+ ##
245
+ # Returns the absolute path to a directory under {#cache_home}, creating
246
+ # it if it doesn't already exist.
247
+ #
248
+ # @param path [String] The relative path to the subdir within the base
249
+ # cache directory.
250
+ # @return [String] The absolute path to the subdir.
251
+ # @raise [Errno::EEXIST] If a non-directory already exists there
252
+ #
253
+ def ensure_cache_subdir(path)
254
+ ensure_subdir_internal(cache_home, path)
255
+ end
256
+
257
+ private
258
+
259
+ def validate_dir_env(name)
260
+ path = @env[name].to_s
261
+ !path.empty? && Compat.absolute_path?(path) ? path : nil
262
+ end
263
+
264
+ def validate_dirs_env(name)
265
+ validate_dirs(@env[name].to_s.split(::File::PATH_SEPARATOR))
266
+ end
267
+
268
+ def validate_dirs(paths)
269
+ paths = paths.find_all { |path| Compat.absolute_path?(path) }
270
+ paths.empty? ? nil : paths
271
+ end
272
+
273
+ def lookup_internal(dirs, path, types)
274
+ results = []
275
+ types = Array(types).map(&:to_s)
276
+ dirs.each do |dir|
277
+ to_check = ::File.join(dir, path)
278
+ stat = ::File.stat(to_check) rescue nil # rubocop:disable Style/RescueModifier
279
+ if stat&.readable? && (types.include?("any") || types.include?(stat.ftype))
280
+ results << to_check
281
+ end
282
+ end
283
+ results
284
+ end
285
+
286
+ def ensure_subdir_internal(base_dir, path)
287
+ path = ::File.join(base_dir, path)
288
+ ::FileUtils.mkdir_p(path, mode: 0o700)
289
+ path
290
+ end
291
+ end
292
+ end
293
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.5
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-28 00:00:00.000000000 Z
11
+ date: 2021-08-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Toys-Core is the command line tool framework underlying Toys. It can
14
14
  be used to create command line executables using the Toys DSL and classes.
@@ -31,8 +31,10 @@ files:
31
31
  - lib/toys/completion.rb
32
32
  - lib/toys/context.rb
33
33
  - lib/toys/core.rb
34
+ - lib/toys/dsl/base.rb
34
35
  - lib/toys/dsl/flag.rb
35
36
  - lib/toys/dsl/flag_group.rb
37
+ - lib/toys/dsl/internal.rb
36
38
  - lib/toys/dsl/positional_arg.rb
37
39
  - lib/toys/dsl/tool.rb
38
40
  - lib/toys/errors.rb
@@ -44,6 +46,7 @@ files:
44
46
  - lib/toys/mixin.rb
45
47
  - lib/toys/module_lookup.rb
46
48
  - lib/toys/positional_arg.rb
49
+ - lib/toys/settings.rb
47
50
  - lib/toys/source_info.rb
48
51
  - lib/toys/standard_middleware/add_verbosity_flags.rb
49
52
  - lib/toys/standard_middleware/apply_config.rb
@@ -55,24 +58,28 @@ files:
55
58
  - lib/toys/standard_mixins/exec.rb
56
59
  - lib/toys/standard_mixins/fileutils.rb
57
60
  - lib/toys/standard_mixins/gems.rb
61
+ - lib/toys/standard_mixins/git_cache.rb
58
62
  - lib/toys/standard_mixins/highline.rb
59
63
  - lib/toys/standard_mixins/terminal.rb
64
+ - lib/toys/standard_mixins/xdg.rb
60
65
  - lib/toys/template.rb
61
- - lib/toys/tool.rb
66
+ - lib/toys/tool_definition.rb
62
67
  - lib/toys/utils/completion_engine.rb
63
68
  - lib/toys/utils/exec.rb
64
69
  - lib/toys/utils/gems.rb
70
+ - lib/toys/utils/git_cache.rb
65
71
  - lib/toys/utils/help_text.rb
66
72
  - lib/toys/utils/terminal.rb
73
+ - lib/toys/utils/xdg.rb
67
74
  - lib/toys/wrappable_string.rb
68
75
  homepage: https://github.com/dazuma/toys
69
76
  licenses:
70
77
  - MIT
71
78
  metadata:
72
- changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.11.5/file.CHANGELOG.html
79
+ changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.12.0/file.CHANGELOG.html
73
80
  source_code_uri: https://github.com/dazuma/toys/tree/main/toys-core
74
81
  bug_tracker_uri: https://github.com/dazuma/toys/issues
75
- documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.11.5
82
+ documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.12.0
76
83
  post_install_message:
77
84
  rdoc_options: []
78
85
  require_paths:
@@ -81,14 +88,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
88
  requirements:
82
89
  - - ">="
83
90
  - !ruby/object:Gem::Version
84
- version: 2.3.0
91
+ version: 2.4.0
85
92
  required_rubygems_version: !ruby/object:Gem::Requirement
86
93
  requirements:
87
94
  - - ">="
88
95
  - !ruby/object:Gem::Version
89
96
  version: '0'
90
97
  requirements: []
91
- rubygems_version: 3.1.4
98
+ rubygems_version: 3.1.6
92
99
  signing_key:
93
100
  specification_version: 4
94
101
  summary: Framework for creating command line executables