rematch 3.2.1 → 4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 150055b455fefcd4f49e818c38c57241acc07afb1fa2ce70e22e35c9df9c19b8
4
- data.tar.gz: 52eed7dc443edbc90bb932a48d019d7871fce611d07bff83f663c798716066b0
3
+ metadata.gz: 18b629b32aa8af9d7c9218946f7467630f68e639930942b44a94f5bea4c77f8a
4
+ data.tar.gz: a49dbb4d655e006ab36147a0b94b6b6d125c436f148e2d4dcf876a621ec6f274
5
5
  SHA512:
6
- metadata.gz: 62464af5e1e8f5b770f6a33014eda611d2ad6a835321fba1b9f717e9efad38db36dd5ddf2694b50321f6b2695a2bf191655285b2471a038d62765659d51c872a
7
- data.tar.gz: bf035d5b5ffa367dded7aedb2177a09825476894498f3ab7f5050b9bc05487365e624fcaecc7d4c78b7f4f45e499594518e76b27192967ce75df4b82d690a41d
6
+ metadata.gz: 8c80817ea424ed8be19329fb01bb6d13a027eedf6aa5e3d20175fec32b5e6413eb4fb311f1fbae0c7fbe64a28f0ed90a00b2ab45199d064ac703d67f2af25dad
7
+ data.tar.gz: e2fbe2c78ae7c19f06412256485b14f4511f0c790586943c31aa6cf3a2a1bff7d7da66d1406470069645490a6af0ef89a870896c34fcca02f19774cf815ec509
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rematch'
4
- require 'minitest/spec'
5
4
 
6
5
  # Implement the minitest plugin
7
6
  module Minitest
@@ -23,50 +22,37 @@ module Minitest
23
22
  # Create the rematch object for each test
24
23
  def after_setup
25
24
  super
26
- @rematch = Rematch.new(path: method(name).source_location.first,
27
- id: "#{self.class.name}##{name}")
25
+ @rematch = Rematch.new(self)
28
26
  end
29
27
  end
30
28
 
31
29
  # Reopen the minitest module
32
30
  module Assertions
33
31
  # Main assertion
34
- def assert_rematch(key, actual, *args)
32
+ def assert_rematch(actual, *args)
35
33
  assertion = :assert_equal
36
34
  message = nil
37
35
  args.each { |arg| arg.is_a?(Symbol) ? assertion = arg : message = arg }
38
36
  if actual.nil? # use specific assert_nil after deprecation of assert_equal nil
39
- assert_nil @rematch.rematch(key, actual), message
37
+ assert_nil @rematch.rematch(actual), message
40
38
  else
41
- send assertion, @rematch.rematch(key, actual), actual, message # assert that the stored value is the same actual value
39
+ send assertion, @rematch.rematch(actual), actual, message # assert that the stored value is the same actual value
42
40
  end
43
41
  end
44
42
 
45
43
  # Temporarily used to store the actual value, useful for reconciliation of expected changed values
46
- def store_assert_rematch(key, actual, *_args)
47
- @rematch.rematch(key, actual, overwrite: true)
44
+ def store_assert_rematch(actual, *_args)
45
+ @rematch.rematch(actual, overwrite: true)
48
46
  # Always fail after storing, forcing the restore of the original assertion/expectation
49
47
  raise Minitest::Assertion, '[rematch] the value has been stored: remove the "store_" prefix to pass the test'
50
48
  end
51
49
  end
52
- # Reopen the Minitest::Expectation class for Minitest 6 compatibility
53
- # or use infect_an_assertion for Minitest 5
54
- require 'minitest/spec'
55
50
 
56
- if defined?(Expectations) && Expectations.respond_to?(:infect_an_assertion)
57
- module Expectations # rubocop:disable Style/Documentation
58
- infect_an_assertion :assert_rematch, :must_rematch
59
- infect_an_assertion :store_assert_rematch, :store_must_rematch
60
- end
61
- else
62
- class Expectation # rubocop:disable Style/Documentation
63
- def must_rematch(key, *)
64
- ctx.assert_rematch(key, target, *)
65
- end
66
-
67
- def store_must_rematch(key, *)
68
- ctx.store_assert_rematch(key, target, *)
69
- end
70
- end
51
+ # Register expectations only if minitest/spec is loaded; ensure the right class in 6.0 and < 6.0
52
+ if (expectation_class = defined?(Spec) && (defined?(Expectation) ? Expectation : Expectations))
53
+ expectation_class.infect_an_assertion :assert_rematch, :must_rematch, :reverse
54
+ expectation_class.alias_method :to_rematch, :must_rematch # to use with expect().to_rematch
55
+ expectation_class.infect_an_assertion :store_assert_rematch, :store_must_rematch, :reverse
56
+ expectation_class.alias_method :store_to_rematch, :store_must_rematch # to use with expect().store_to_rematch
71
57
  end
72
58
  end
data/lib/rematch.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fileutils'
4
+ require 'digest/sha1'
4
5
  require_relative 'rematch/store'
5
6
 
6
7
  # Handles the key/value store for each test
7
8
  class Rematch
8
- VERSION = '3.2.1'
9
+ VERSION = '4.0.0'
9
10
  CONFIG = { ext: '.yaml' } # rubocop:disable Style/MutableConstant
10
11
 
11
12
  @rebuild = false # rebuild the store?
@@ -26,35 +27,70 @@ class Rematch
26
27
  end
27
28
 
28
29
  # Instantiated at each test, stores the path and the unique id of the test being run
29
- def initialize(path:, id:)
30
- store_path = "#{path}#{CONFIG[:ext]}"
30
+ def initialize(test)
31
+ @path = test.method(test.name).source_location.first
32
+ store_path = "#{@path}#{CONFIG[:ext]}"
31
33
  self.class.check_rebuild(store_path)
32
- @store = Store.new(store_path, true)
33
- @id = id.tr('#: ', '_') # easier key string for clumsy yaml parsers/highlighters
34
+ @store = Store.new(store_path, true)
35
+ @id = test_uid(test.class.name, test.name)
34
36
  end
35
37
 
36
38
  # Retrieve the stored value for the current assertion if its key is known; store the value otherwise
37
- def rematch(key, value, overwrite: nil)
38
- # key = assertion_key(key)
39
+ def rematch(value, overwrite: nil)
40
+ key = assertion_key
39
41
  @store.transaction do |s|
40
- if s.root?(@id) # there is the root id
41
- if s[@id].key?(key) && !overwrite # there is the key and not overwrite
42
- s[@id][key] # return it
43
- else # not such a key
44
- s[@id][key] = value # set it
45
- store_warning(key) unless overwrite
46
- value
47
- end
48
- else # there is no root yet
49
- s[@id] = { key => value } # the key is the first one
50
- store_warning(key)
51
- value # always return the value
42
+ if s.root?(key) && !overwrite # there is the key and not overwrite
43
+ s[key] # return it
44
+ else # no such key or overwrite
45
+ s[key] = value # set it
46
+ tidy_store(s) # sort keys and cleanup orphans
47
+ store_warning(key) unless overwrite
48
+ value
52
49
  end
53
50
  end
54
51
  end
55
52
 
56
53
  def store_warning(key)
57
- warn "Rematch stored new value for: #{key.inspect}\n#{@id}\n#{@store.path}\n\n" \
58
- unless Rematch.skip_warning
54
+ warn "Rematch stored new value for: #{key.inspect}\n#{@store.path}\n\n" \
55
+ unless Rematch.skip_warning
56
+ end
57
+
58
+ protected
59
+
60
+ # Generate the unique id for the test (SHA1)
61
+ def test_uid(class_name, method_name)
62
+ Digest::SHA1.hexdigest("#{class_name}#{method_name}")
63
+ end
64
+
65
+ private
66
+
67
+ # Generate the key based on the line number and test ID
68
+ def assertion_key
69
+ line = caller_locations.find { |l| l.path == @path }&.lineno
70
+ "L#{line} #{@id}"
71
+ end
72
+
73
+ # Ensure the keys are sorted by the order of the tests in the file
74
+ # and remove keys that do not match any existing test (orphans)
75
+ def tidy_store(store)
76
+ # Optimization: only if Minitest is loaded
77
+ return unless defined?(Minitest::Runnable)
78
+
79
+ # Get all valid SHA1s from all runnables in this file
80
+ valid_ids = []
81
+ Minitest::Runnable.runnables.each do |runnable|
82
+ runnable.runnable_methods.each do |method_name|
83
+ file, = runnable.instance_method(method_name).source_location
84
+ next unless file == @path # @path is the test file path
85
+
86
+ valid_ids << test_uid(runnable.name, method_name)
87
+ end
88
+ end
89
+ # Extract all data
90
+ data = store.roots.to_h { |key| [key, store.delete(key)] }
91
+ # Re-add data in the correct order, filter orphans and sort
92
+ data.select { |key, _| valid_ids.include?(key.split.last) }
93
+ .sort_by { |key, _| key[/L(\d+)/, 1].to_i }
94
+ .each { |key, value| store[key] = value }
59
95
  end
60
96
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rematch
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Domizio Demichelis
@@ -37,9 +37,9 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
- description: Instead of copying and pasting large outputs or big ruby structures into
41
- all the affected test files every time your code change, you can do it the easy
42
- way, possibly saving many hours of boring maintenance work!
40
+ description: Rematch declutters your tests by storing large outputs or structures
41
+ in separate files. It verifies them automatically and enables easy updates when
42
+ your code changes, eliminating tedious copy-paste maintenance.
43
43
  email:
44
44
  - dd.nexus@gmail.com
45
45
  executables: []
@@ -74,6 +74,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
74
  requirements: []
75
75
  rubygems_version: 3.6.9
76
76
  specification_version: 4
77
- summary: Declutter your test files from large hardcoded data and update them automatically
78
- when your code changes
77
+ summary: Declutter your test files and automatically update expected values.
79
78
  test_files: []