rematch 3.2.2 → 4.1.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: 517a212dcc929a169a02fb05526206e64ee3aa4f9a9dced33a76d0c0bca20698
4
- data.tar.gz: e1764805c826d287daf49ec747551e86a8736ba2cc572e5a3d7b7eea9fe3d923
3
+ metadata.gz: 75263525e5c9f837c2308c800c6ddd0fa8efa1472dc32aaca3eab36d4ba20990
4
+ data.tar.gz: 734123f5f1ea481c6d1d8c31cddfa074483e5dbd7289e6f8965f33a724b0eb46
5
5
  SHA512:
6
- metadata.gz: b6ab2b85043428cd063a52ceaf93bd5e5a05a73d8d795946bdfdffecffdb19e7c4b545ea076cc633e87038947074d657574ebe0914fd254eff633c770a8444a6
7
- data.tar.gz: 38a7dd07e06f54900417e7a8344a34777a018380c157342f741f3b956bbb3bbcc24b900692dd9f096437fa7d75df55de0de8fe904c44bc7cd0249a96f56a36db
6
+ metadata.gz: 3c54c23fd47f85a99f53c3d7deead137ce5b5fcfaa2559ce957c440f2f142246091fde3aa1b6a0407029377519f28751c45ce0f42a8abd02c035843121a857d3
7
+ data.tar.gz: d3f5337a74934922e9d889564dc67f18be1a7371a2c24df4699cedb764df30c5278cca25b7872192235c4b4a93b4f044cfbe6393ef4e12bcfa769d7f06dda3bd
@@ -22,28 +22,34 @@ module Minitest
22
22
  # Create the rematch object for each test
23
23
  def after_setup
24
24
  super
25
- @rematch = Rematch.new(path: method(name).source_location.first,
26
- id: "#{self.class.name}##{name}")
25
+ @rematch = Rematch.new(self)
27
26
  end
28
27
  end
29
28
 
30
29
  # Reopen the minitest module
31
30
  module Assertions
32
31
  # Main assertion
33
- def assert_rematch(key, actual, *args)
32
+ def assert_rematch(actual, *args)
33
+ # Extract options (id) if present
34
+ opts = args.last.is_a?(Hash) && args.last.key?(:id) ? args.pop : {}
35
+ id = opts[:id]
34
36
  assertion = :assert_equal
35
37
  message = nil
36
38
  args.each { |arg| arg.is_a?(Symbol) ? assertion = arg : message = arg }
37
39
  if actual.nil? # use specific assert_nil after deprecation of assert_equal nil
38
- assert_nil @rematch.rematch(key, actual), message
40
+ assert_nil @rematch.rematch(actual, id: id), message
39
41
  else
40
- send assertion, @rematch.rematch(key, actual), actual, message # assert that the stored value is the same actual value
42
+ # assert that the stored value is the same actual value
43
+ send assertion, @rematch.rematch(actual, id: id), actual, message
41
44
  end
42
45
  end
43
46
 
44
47
  # Temporarily used to store the actual value, useful for reconciliation of expected changed values
45
- def store_assert_rematch(key, actual, *_args)
46
- @rematch.rematch(key, actual, overwrite: true)
48
+ def store_assert_rematch(actual, *args)
49
+ opts = args.last.is_a?(Hash) && args.last.key?(:id) ? args.pop : {}
50
+ id = opts[:id]
51
+
52
+ @rematch.rematch(actual, overwrite: true, id: id)
47
53
  # Always fail after storing, forcing the restore of the original assertion/expectation
48
54
  raise Minitest::Assertion, '[rematch] the value has been stored: remove the "store_" prefix to pass the test'
49
55
  end
@@ -51,7 +57,9 @@ module Minitest
51
57
 
52
58
  # Register expectations only if minitest/spec is loaded; ensure the right class in 6.0 and < 6.0
53
59
  if (expectation_class = defined?(Spec) && (defined?(Expectation) ? Expectation : Expectations))
54
- expectation_class.infect_an_assertion :assert_rematch, :must_rematch
55
- expectation_class.infect_an_assertion :store_assert_rematch, :store_must_rematch
60
+ expectation_class.infect_an_assertion :assert_rematch, :must_rematch, :reverse
61
+ expectation_class.alias_method :to_rematch, :must_rematch # to use with expect().to_rematch
62
+ expectation_class.infect_an_assertion :store_assert_rematch, :store_must_rematch, :reverse
63
+ expectation_class.alias_method :store_to_rematch, :store_must_rematch
56
64
  end
57
65
  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.2'
9
+ VERSION = '4.1.0'
9
10
  CONFIG = { ext: '.yaml' } # rubocop:disable Style/MutableConstant
10
11
 
11
12
  @rebuild = false # rebuild the store?
@@ -26,35 +27,69 @@ 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, id: nil)
40
+ key = assertion_key(id)
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" unless Rematch.skip_warning
55
+ end
56
+
57
+ protected
58
+
59
+ # Generate the unique id for the test (SHA1)
60
+ def test_uid(class_name, method_name)
61
+ Digest::SHA1.hexdigest("#{class_name}#{method_name}")
62
+ end
63
+
64
+ private
65
+
66
+ # Generate the key based on the line number, optional id, and test ID
67
+ def assertion_key(id)
68
+ line = caller_locations.find { |l| l.path == @path }&.lineno
69
+ %(L#{line}#{" [#{id}]" if id} #{@id})
70
+ end
71
+
72
+ # Ensure the keys are sorted by the order of the tests in the file
73
+ # and remove keys that do not match any existing test (orphans)
74
+ def tidy_store(store)
75
+ # Optimization: only if Minitest is loaded
76
+ return unless defined?(Minitest::Runnable)
77
+
78
+ # Get all valid SHA1s from all runnables in this file
79
+ valid_ids = []
80
+ Minitest::Runnable.runnables.each do |runnable|
81
+ runnable.runnable_methods.each do |method_name|
82
+ file, = runnable.instance_method(method_name).source_location
83
+ next unless file == @path # @path is the test file path
84
+
85
+ valid_ids << test_uid(runnable.name, method_name)
86
+ end
87
+ end
88
+ # Extract all data
89
+ data = store.roots.to_h { |key| [key, store.delete(key)] }
90
+ # Re-add data in the correct order, filter orphans, and sort
91
+ data.select { |key, _| valid_ids.include?(key.split.last) }
92
+ .sort_by { |key, _| key[/L(\d+)/, 1].to_i }
93
+ .each { |key, value| store[key] = value }
59
94
  end
60
95
  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.2
4
+ version: 4.1.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: []