tldr 0.10.1 → 1.0.0

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.
@@ -1,25 +1,26 @@
1
1
  class TLDR
2
2
  CONFLAGS = {
3
- seed: "--seed",
4
- no_helper: "--no-helper",
5
- verbose: "--verbose",
6
- print_interrupted_test_backtraces: "--print-interrupted-test-backtraces",
7
- reporter: "--reporter",
8
- helper_paths: "--helper",
9
- load_paths: "--load-path",
3
+ timeout: "--[no-]timeout",
4
+ watch: "--watch",
5
+ fail_fast: "--fail-fast",
10
6
  parallel: "--[no-]parallel",
7
+ seed: "--seed",
11
8
  names: "--name",
12
- fail_fast: "--fail-fast",
13
- no_emoji: "--no-emoji",
9
+ exclude_names: "--exclude-name",
10
+ exclude_paths: "--exclude-path",
11
+ helper_paths: "--helper",
12
+ no_helper: "--no-helper",
14
13
  prepend_paths: "--prepend",
15
14
  no_prepend: "--no-prepend",
16
- exclude_paths: "--exclude-path",
17
- exclude_names: "--exclude-name",
15
+ load_paths: "--load-path",
18
16
  base_path: "--base-path",
19
- no_dotfile: "--no-dotfile",
17
+ config_path: "--[no-]config",
18
+ reporter: "--reporter",
19
+ emoji: "--[no-]emoji",
20
20
  warnings: "--[no-]warnings",
21
- watch: "--watch",
21
+ verbose: "--verbose",
22
22
  yes_i_know: "--yes-i-know",
23
+ print_interrupted_test_backtraces: "--print-interrupted-test-backtraces",
23
24
  i_am_being_watched: "--i-am-being-watched",
24
25
  paths: nil
25
26
  }.freeze
@@ -27,20 +28,23 @@ class TLDR
27
28
  PATH_FLAGS = [:paths, :helper_paths, :load_paths, :prepend_paths, :exclude_paths].freeze
28
29
  MOST_RECENTLY_MODIFIED_TAG = "MOST_RECENTLY_MODIFIED".freeze
29
30
  CONFIG_ATTRIBUTES = [
30
- :paths, :seed, :no_helper, :verbose, :print_interrupted_test_backtraces, :reporter,
31
- :helper_paths, :load_paths, :parallel, :names, :fail_fast, :no_emoji,
32
- :prepend_paths, :no_prepend, :exclude_paths, :exclude_names, :base_path,
33
- :no_dotfile, :warnings, :watch, :yes_i_know, :i_am_being_watched,
31
+ :timeout, :watch, :fail_fast, :parallel, :seed, :names, :exclude_names,
32
+ :exclude_paths, :helper_paths, :no_helper, :prepend_paths, :no_prepend,
33
+ :load_paths, :base_path, :config_path, :reporter, :emoji, :warnings,
34
+ :verbose, :yes_i_know, :print_interrupted_test_backtraces,
35
+ :i_am_being_watched, :paths,
34
36
  # Internal properties
35
37
  :config_intended_for_merge_only, :seed_set_intentionally, :cli_defaults
36
38
  ].freeze
37
39
 
38
40
  Config = Struct.new(*CONFIG_ATTRIBUTES, keyword_init: true) do
39
41
  def initialize(**args)
42
+ @argv_reconstructor = ArgvReconstructor.new
43
+
40
44
  original_base_path = Dir.pwd
41
45
  unless args[:config_intended_for_merge_only]
42
46
  change_working_directory_because_i_am_bad_and_i_should_feel_bad!(args[:base_path])
43
- args = merge_dotfile_args(args) unless args[:no_dotfile]
47
+ args = merge_dotfile_args(args) unless args[:config_path].nil?
44
48
  end
45
49
  args = undefault_parallel_if_seed_set(args)
46
50
  unless args[:config_intended_for_merge_only]
@@ -54,50 +58,53 @@ class TLDR
54
58
  # These are for internal tracking and resolved at initialization-time
55
59
  undef_method :config_intended_for_merge_only=, :seed_set_intentionally=,
56
60
  # These must be set when the Config is first initialized
57
- :cli_defaults=, :no_dotfile=, :base_path=
61
+ :cli_defaults=, :config_path=, :base_path=
58
62
 
59
63
  def self.build_defaults cli_defaults: true
60
64
  common = {
61
- seed: rand(10_000),
62
- no_helper: false,
63
- verbose: false,
64
- print_interrupted_test_backtraces: false,
65
- reporter: Reporters::Default,
65
+ timeout: -1,
66
+ watch: false,
67
+ fail_fast: false,
66
68
  parallel: true,
69
+ seed: rand(10_000),
67
70
  names: [],
68
- fail_fast: false,
69
- no_emoji: false,
70
- no_prepend: false,
71
- exclude_paths: [],
72
71
  exclude_names: [],
72
+ exclude_paths: [],
73
+ no_helper: false,
74
+ no_prepend: false,
73
75
  base_path: nil,
76
+ reporter: "TLDR::Reporters::Default",
77
+ emoji: false,
74
78
  warnings: true,
75
- watch: false,
79
+ verbose: false,
76
80
  yes_i_know: false,
81
+ print_interrupted_test_backtraces: false,
77
82
  i_am_being_watched: false
78
83
  }
79
84
 
80
85
  if cli_defaults
81
86
  common.merge(
82
- paths: Dir["test/**/*_test.rb", "test/**/test_*.rb"],
83
87
  helper_paths: ["test/helper.rb"],
88
+ prepend_paths: [MOST_RECENTLY_MODIFIED_TAG],
84
89
  load_paths: ["lib", "test"],
85
- prepend_paths: [MOST_RECENTLY_MODIFIED_TAG]
90
+ config_path: nil,
91
+ paths: Dir["test/**/*_test.rb", "test/**/test_*.rb"]
86
92
  )
87
93
  else
88
94
  common.merge(
89
- paths: [],
90
95
  helper_paths: [],
96
+ prepend_paths: [],
91
97
  load_paths: [],
92
- prepend_paths: []
98
+ config_path: Config::DEFAULT_YAML_PATH, # ArgvParser#parse will set this default and if it sets nil that is intentionally blank b/c --no-config
99
+ paths: []
93
100
  )
94
101
  end
95
102
  end
96
103
 
97
104
  def undefault_parallel_if_seed_set args
98
105
  args.merge(
99
- seed_set_intentionally: !args[:seed].nil?,
100
- parallel: (args[:parallel].nil? ? args[:seed].nil? : args[:parallel])
106
+ parallel: (args[:parallel].nil? ? args[:seed].nil? : args[:parallel]),
107
+ seed_set_intentionally: !args[:seed].nil?
101
108
  )
102
109
  end
103
110
 
@@ -106,17 +113,17 @@ class TLDR
106
113
  defaults = Config.build_defaults(cli_defaults: merged_args[:cli_defaults])
107
114
 
108
115
  # Arrays
109
- [:paths, :helper_paths, :load_paths, :names, :prepend_paths, :exclude_paths, :exclude_names].each do |key|
116
+ [:names, :exclude_names, :exclude_paths, :helper_paths, :prepend_paths, :load_paths, :paths].each do |key|
110
117
  merged_args[key] = defaults[key] if merged_args[key].nil? || merged_args[key].empty?
111
118
  end
112
119
 
113
120
  # Booleans
114
- [:no_helper, :verbose, :print_interrupted_test_backtraces, :fail_fast, :no_emoji, :no_prepend, :warnings, :yes_i_know, :i_am_being_watched].each do |key|
121
+ [:watch, :fail_fast, :parallel, :no_helper, :no_prepend, :emoji, :warnings, :verbose, :yes_i_know, :print_interrupted_test_backtraces, :i_am_being_watched].each do |key|
115
122
  merged_args[key] = defaults[key] if merged_args[key].nil?
116
123
  end
117
124
 
118
125
  # Values
119
- [:seed, :reporter].each do |key|
126
+ [:timeout, :seed, :base_path, :config_path, :reporter].each do |key|
120
127
  merged_args[key] ||= defaults[key]
121
128
  end
122
129
 
@@ -147,87 +154,20 @@ class TLDR
147
154
  end
148
155
 
149
156
  def to_full_args exclude: [], ensure_args: [], exclude_dotfile_matches: false
150
- argv = to_cli_argv(
151
- CONFLAGS.keys - exclude - [
152
- (:seed unless seed_set_intentionally),
153
- :watch,
154
- :i_am_being_watched
155
- ],
156
- exclude_dotfile_matches:
157
- )
158
-
159
- ensure_args.each do |arg|
160
- argv << arg unless argv.include?(arg)
161
- end
162
-
163
- argv.join(" ")
157
+ @argv_reconstructor.reconstruct(self, exclude:, ensure_args:, exclude_dotfile_matches:)
164
158
  end
165
159
 
166
160
  def to_single_path_args path, exclude_dotfile_matches: false
167
- argv = to_cli_argv(CONFLAGS.keys - [
168
- :seed, :parallel, :names, :fail_fast, :paths, :prepend_paths,
169
- :no_prepend, :exclude_paths, :watch, :i_am_being_watched
170
- ], exclude_dotfile_matches:)
171
-
172
- (argv + [stringify(:paths, path)]).join(" ")
161
+ @argv_reconstructor.reconstruct_single_path_args(self, path, exclude_dotfile_matches:)
173
162
  end
174
163
 
175
- private
176
-
177
- def to_cli_argv options = CONFLAGS.keys, exclude_dotfile_matches:
178
- defaults = Config.build_defaults(cli_defaults: true)
179
- defaults = defaults.merge(dotfile_args) if exclude_dotfile_matches
180
- options.map { |key|
181
- flag = CONFLAGS[key]
182
-
183
- # Special cases
184
- if key == :prepend_paths
185
- if prepend_paths.map { |s| stringify(key, s) }.sort == paths.map { |s| stringify(:paths, s) }.sort
186
- # Don't print prepended tests if they're the same as the test paths
187
- next
188
- elsif no_prepend
189
- # Don't print prepended tests if they're disabled
190
- next
191
- end
192
- elsif key == :helper_paths && no_helper
193
- # Don't print the helper if it's disabled
194
- next
195
- elsif key == :parallel
196
- val = if !seed_set_intentionally && !parallel
197
- "--no-parallel"
198
- elsif !seed.nil? && seed_set_intentionally && parallel
199
- "--parallel"
200
- end
201
- next val
202
- elsif key == :warnings && defaults[:warnings] != self[:warnings]
203
- next warnings ? "--warnings" : "--no-warnings"
204
- end
164
+ def dotfile_args config_path
165
+ return {} unless File.exist?(config_path)
205
166
 
206
- if defaults[key] == self[key] && (key != :seed || !seed_set_intentionally)
207
- next
208
- elsif self[key].is_a?(Array)
209
- self[key].map { |value| [flag, stringify(key, value)] }
210
- elsif self[key].is_a?(TrueClass) || self[key].is_a?(FalseClass)
211
- flag if self[key]
212
- elsif self[key].is_a?(Class)
213
- [flag, self[key].name]
214
- elsif !self[key].nil?
215
- [flag, stringify(key, self[key])]
216
- end
217
- }.flatten.compact
167
+ @dotfile_args ||= YamlParser.new.parse(config_path)
218
168
  end
219
169
 
220
- def stringify key, val
221
- if PATH_FLAGS.include?(key) && val.start_with?(Dir.pwd)
222
- val = val[Dir.pwd.length + 1..]
223
- end
224
-
225
- if val.nil? || val.is_a?(Integer)
226
- val
227
- else
228
- "\"#{val}\""
229
- end
230
- end
170
+ private
231
171
 
232
172
  def most_recently_modified_test_file tests
233
173
  return if tests.empty?
@@ -248,28 +188,10 @@ class TLDR
248
188
  end
249
189
 
250
190
  def merge_dotfile_args args
251
- return args if args[:no_dotfile]
252
-
253
- dotfile_args.merge(args)
254
- end
255
-
256
- def dotfile_args
257
- return {} unless File.exist?(".tldr.yml")
258
-
259
- require "yaml"
260
- @dotfile_args ||= YAML.load_file(".tldr.yml").transform_keys { |k| k.to_sym }.tap do |dotfile_args|
261
- # Since we don't have shell expansion, we have to glob any paths ourselves
262
- if dotfile_args.key?(:paths)
263
- dotfile_args[:paths] = dotfile_args[:paths].flat_map { |path| Dir[path] }
264
- end
265
- # The argv parser normally does this:
266
- if dotfile_args.key?(:reporter)
267
- dotfile_args[:reporter] = Kernel.const_get(dotfile_args[:reporter])
268
- end
269
- if (invalid_args = dotfile_args.except(*CONFIG_ATTRIBUTES)).any?
270
- raise Error, "Invalid keys in .tldr.yml file: #{invalid_args.keys.join(", ")}"
271
- end
272
- end
191
+ dotfile_args(args[:config_path]).merge(args)
273
192
  end
274
193
  end
194
+
195
+ Config::DEFAULT_YAML_PATH = ".tldr.yml"
196
+ Config::DEFAULT_TIMEOUT = 1.8
275
197
  end
data/lib/tldr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class TLDR
2
- VERSION = "0.10.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,29 @@
1
+ require "optparse"
2
+
3
+ class TLDR
4
+ class YamlParser
5
+ def parse path
6
+ require "yaml"
7
+ YAML.load_file(path)
8
+ .transform_keys { |k| k.to_sym }
9
+ .tap do |dotfile_args|
10
+ # Since we don't have shell expansion, we have to glob any paths ourselves
11
+ if dotfile_args.key?(:paths)
12
+ dotfile_args[:paths] = dotfile_args[:paths].flat_map { |path| Dir[path] }
13
+ end
14
+ if dotfile_args.key?(:timeout)
15
+ dotfile_args[:timeout] = case dotfile_args[:timeout]
16
+ when true then Config::DEFAULT_TIMEOUT
17
+ when false then -1
18
+ when String then Float(dotfile_args[:timeout])
19
+ else dotfile_args[:timeout]
20
+ end
21
+ end
22
+
23
+ if (invalid_args = dotfile_args.except(*CONFIG_ATTRIBUTES)).any?
24
+ raise Error, "Invalid keys in #{File.basename(path)} file: #{invalid_args.keys.join(", ")}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/tldr.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "concurrent-ruby"
2
2
 
3
3
  require_relative "tldr/argv_parser"
4
+ require_relative "tldr/argv_reconstructor"
4
5
  require_relative "tldr/assertions"
5
6
  require_relative "tldr/backtrace_filter"
6
7
  require_relative "tldr/class_util"
@@ -10,6 +11,7 @@ require_relative "tldr/hooks"
10
11
  require_relative "tldr/parallel_controls"
11
12
  require_relative "tldr/path_util"
12
13
  require_relative "tldr/planner"
14
+ require_relative "tldr/minitest_compatibility"
13
15
  require_relative "tldr/reporters"
14
16
  require_relative "tldr/ruby_util"
15
17
  require_relative "tldr/runner"
@@ -19,6 +21,7 @@ require_relative "tldr/strategizer"
19
21
  require_relative "tldr/value"
20
22
  require_relative "tldr/version"
21
23
  require_relative "tldr/watcher"
24
+ require_relative "tldr/yaml_parser"
22
25
 
23
26
  class TLDR
24
27
  include Assertions
data/script/test CHANGED
@@ -5,10 +5,10 @@ set -e
5
5
  bundle exec rake
6
6
 
7
7
  cd example/a
8
- bundle exec tldr | grep "😁😁"
8
+ bundle exec tldr | ruby -e 'exit(ARGF.read =~ /\n..\n/ ? 0 : 1)'
9
9
  cd ../..
10
10
 
11
11
  cd example/b
12
- bundle exec ruby -Itest test/some_test.rb | grep "😁"
13
- bundle exec rake tldr | grep "😁"
12
+ bundle exec ruby -Itest test/some_test.rb | ruby -e 'exit(ARGF.read =~ /\n.\n/ ? 0 : 1)'
13
+ bundle exec rake tldr | ruby -e 'exit(ARGF.read =~ /\n.\n/ ? 0 : 1)'
14
14
  cd ../..
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tldr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  - Aaron Patterson
9
+ autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2025-03-26 00:00:00.000000000 Z
12
+ date: 2025-04-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: super_diff
@@ -38,6 +39,21 @@ dependencies:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
41
  version: '1.2'
42
+ - !ruby/object:Gem::Dependency
43
+ name: irb
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.10'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.10'
56
+ description:
41
57
  email:
42
58
  - searls@gmail.com
43
59
  - tenderlove@ruby-lang.org
@@ -56,13 +72,15 @@ files:
56
72
  - exe/tldt
57
73
  - lib/tldr.rb
58
74
  - lib/tldr/argv_parser.rb
75
+ - lib/tldr/argv_reconstructor.rb
59
76
  - lib/tldr/assertions.rb
60
- - lib/tldr/assertions/minitest_compatibility.rb
77
+ - lib/tldr/autorun.rb
61
78
  - lib/tldr/backtrace_filter.rb
62
79
  - lib/tldr/class_util.rb
63
80
  - lib/tldr/error.rb
64
81
  - lib/tldr/executor.rb
65
82
  - lib/tldr/hooks.rb
83
+ - lib/tldr/minitest_compatibility.rb
66
84
  - lib/tldr/parallel_controls.rb
67
85
  - lib/tldr/path_util.rb
68
86
  - lib/tldr/planner.rb
@@ -86,6 +104,7 @@ files:
86
104
  - lib/tldr/value/wip_test.rb
87
105
  - lib/tldr/version.rb
88
106
  - lib/tldr/watcher.rb
107
+ - lib/tldr/yaml_parser.rb
89
108
  - script/setup
90
109
  - script/test
91
110
  - script/upgrade
@@ -96,6 +115,7 @@ metadata:
96
115
  homepage_uri: https://github.com/tendersearls/tldr
97
116
  source_code_uri: https://github.com/tendersearls/tldr
98
117
  changelog_uri: https://github.com/tendersearls/tldr/blob/main/CHANGELOG.md
118
+ post_install_message:
99
119
  rdoc_options: []
100
120
  require_paths:
101
121
  - lib
@@ -110,7 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
130
  - !ruby/object:Gem::Version
111
131
  version: '0'
112
132
  requirements: []
113
- rubygems_version: 3.6.2
133
+ rubygems_version: 3.3.27
134
+ signing_key:
114
135
  specification_version: 4
115
136
  summary: TLDR will run your tests, but only for 1.8 seconds.
116
137
  test_files: []
@@ -1,55 +0,0 @@
1
- # These methods are provided only for drop-in compatibility with Minitest:
2
- #
3
- # require "tldr/assertions/minitest"
4
- #
5
- # Will load these methods for use in your tests
6
- #
7
- # While all the methods in this file were written for TLDR, they were designed
8
- # to maximize compatibility with minitest's assertions API and messages here:
9
- #
10
- # https://github.com/minitest/minitest/blob/master/lib/minitest/assertions.rb
11
- #
12
- # As a result, many implementations are extremely similar to those found in
13
- # minitest. Any such implementations are Copyright © Ryan Davis, seattle.rb and
14
- # distributed under the MIT License
15
-
16
- class TLDR
17
- module Assertions
18
- module MinitestCompatibility
19
- def assert_includes actual, expected, message = nil
20
- assert_include? expected, actual, message
21
- end
22
-
23
- def refute_includes actual, expected, message = nil
24
- refute_include? expected, actual, message
25
- end
26
-
27
- def assert_send receiver_method_args, message = nil
28
- warn "DEPRECATED: assert_send. From #{TLDR.filter_backtrace(caller).first}"
29
- receiver, method, *args = receiver_method_args
30
- message = Assertions.msg(message) {
31
- "Expected #{Assertions.h(receiver)}.#{method}(*#{Assertions.h(args)}) to return true"
32
- }
33
-
34
- assert receiver.__send__(method, *args), message
35
- end
36
-
37
- def capture_io &blk
38
- Assertions.capture_io(&blk)
39
- end
40
-
41
- def mu_pp obj
42
- s = obj.inspect.encode(Encoding.default_external)
43
-
44
- if String === obj && (obj.encoding != Encoding.default_external ||
45
- !obj.valid_encoding?)
46
- enc = "# encoding: #{obj.encoding}"
47
- val = "# valid: #{obj.valid_encoding?}"
48
- "#{enc}\n#{val}\n#{s}"
49
- else
50
- s
51
- end
52
- end
53
- end
54
- end
55
- end