mini_sanity 1.1.0 → 3.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
- SHA1:
3
- metadata.gz: ba5fd068770843f3044c6dade58282429e1330e5
4
- data.tar.gz: 1aa0bfd6c7f88f9a0e1d992cd18289c5a9328ac1
2
+ SHA256:
3
+ metadata.gz: 38c674ac249e8e02d75079e3ecd9a96150f4f6fbc4d8211a46f1837e9d8d4198
4
+ data.tar.gz: cc578b1ff20709384a8a322b44378a828020d3b3391a539853ba0d8121e3c73e
5
5
  SHA512:
6
- metadata.gz: 1dfe7f149c334cd7ea7eb2330ddf834b72a17bb3009cdd45f42dd8e1347278084a65c835a1d7d438e881a64d42750ab8fda3eefd0fb498a919e76faa38a59bd3
7
- data.tar.gz: ea9642cd25487cf2f54d990843f79ba98cece1b560811ea791644e2c010707d910fb5def8800a9518bec04ef274b9db7bf9884a1612549888ee6dfe5102f7260
6
+ metadata.gz: 58a408bd36b1ac28b54f987b1645d28edc96162b2067dd703be26d98a4fc91ac4a6c6d1a452ffcccf65ec064ecbbb0f44b0729b33d658bee48f56435931bfd2d
7
+ data.tar.gz: 0f21c36082f440825eee69a2c36051e78a28077c961b5e4b558d9bf6bccf5d322f27b73beffc576969e87466241f921d0c175ea6621f30f1943f636879b50255
data/CHANGELOG.md CHANGED
@@ -1,30 +1,71 @@
1
+ ## 3.0.0
2
+
3
+ * [BREAKING] Drop support for Ruby < 3.4
4
+
5
+
6
+ ## 2.0.0
7
+
8
+ * [BREAKING] Drop support for Ruby < 2.6
9
+ * Add `Object#assert!`
10
+ * Add `Object#refute!`
11
+ * [BREAKING] Remove methods obsoleted by `Object#assert!` and `#refute!`
12
+ * `Object#assert_equal!` => `assert!(expected *OR* Set[expected])`
13
+ * `Object#assert_in!` => `assert!(permitted.to_set)`
14
+ * `Object#assert_instance_of!` => `assert!(Set[klass], &:class)`
15
+ * `Object#assert_kind_of!` => `assert!(klass)`
16
+ * `Object#assert_nil!` => `assert!(nil)`
17
+ * `Object#refute_equal!` => `refute!(forbidden *OR* Set[forbidden])`
18
+ * `Object#refute_in!` => `refute!(prohibited.to_set)`
19
+ * `Object#refute_nil!` => `refute!(nil)`
20
+ * `Array#assert_length!` => `assert!(permitted_length, &:length)`
21
+ * `Array#refute_length!` => `refute!(prohibited_length, &:length)`
22
+ * `Enumerable#assert_empty!` => `assert!(&:empty?)`
23
+ * `Enumerable#refute_empty!` => `refute!(&:empty?)`
24
+ * `String#assert_empty!` => `assert!(&:empty?)`
25
+ * `String#assert_length!` => `assert!(permitted_length, &:length)`
26
+ * `String#assert_match!` => `assert!(pattern)`
27
+ * `String#refute_empty!` => `refute!(&:empty?)`
28
+ * `String#refute_length!` => `refute!(prohibited_length, &:length)`
29
+ * `String#refute_match!` => `refute!(pattern)`
30
+ * `Pathname#assert_dir!` => `assert!(&:directory?)`
31
+ * `Pathname#assert_exist!` => `assert!(&:exist?)`
32
+ * `Pathname#assert_file!` => `assert!(&:file?)`
33
+ * `Pathname#refute_dir!` => `refute!(&:directory?)`
34
+ * `Pathname#refute_exist!` => `refute!(&:exist?)`
35
+ * `Pathname#refute_file!` => `refute!(&:file?)`
36
+ * [BREAKING] Remove `Object#assert_respond_to!`
37
+ * [BREAKING] Remove `Enumerable#first!`
38
+ * Replacement for most cases: `assert!(n.., &:size).first(n)`
39
+ * [BREAKING] `require "mini_sanity"` now loads all pieces of the API.
40
+ However, individual pieces of the API can be loaded a la carte via
41
+ e.g. `require "mini_sanity/assert"`.
42
+ * Add `Enumerator#result!`
43
+ * Add `Enumerator#results!`
44
+
45
+
1
46
  ## 1.1.0
2
47
 
3
- * Standardized exception error messages
4
-
5
- * Added sanity check methods:
6
- * Array#assert_length!
7
- * Array#refute_length!
8
- * Enumerable#assert_empty!
9
- * Object#assert_equal!
10
- * Object#assert_in!
11
- * Object#assert_nil!
12
- * Object#refute_equal!
13
- * Object#refute_in!
14
- * Pathname#assert_dir!
15
- * Pathname#assert_file!
16
- * Pathname#refute_dir!
17
- * Pathname#refute_file!
18
- * String#assert_empty!
19
- * String#assert_length!
20
- * String#refute_length!
21
-
22
- * Added utility methods:
23
- * Enumerable#first!
24
- * Regexp#match!
25
- * String#change
26
- * String#change!
27
- * String#match!
48
+ * Standardize error messages
49
+ * Add `Array#assert_length!`
50
+ * Add `Array#refute_length!`
51
+ * Add `Enumerable#assert_empty!`
52
+ * Add `Object#assert_equal!`
53
+ * Add `Object#assert_in!`
54
+ * Add `Object#assert_nil!`
55
+ * Add `Object#refute_equal!`
56
+ * Add `Object#refute_in!`
57
+ * Add `Pathname#assert_dir!`
58
+ * Add `Pathname#assert_file!`
59
+ * Add `Pathname#refute_dir!`
60
+ * Add `Pathname#refute_file!`
61
+ * Add `String#assert_empty!`
62
+ * Add `String#assert_length!`
63
+ * Add `String#refute_length!`
64
+ * Add `Enumerable#first!` (loaded via `require "mini_sanity/util"`)
65
+ * Add `Regexp#match!` (loaded via `require "mini_sanity/util"`)
66
+ * Add `String#change` (loaded via `require "mini_sanity/util"`)
67
+ * Add `String#change!` (loaded via `require "mini_sanity/util"`)
68
+ * Add `String#match!` (loaded via `require "mini_sanity/util"`)
28
69
 
29
70
 
30
71
  ## 1.0.0
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in mini_sanity.gemspec
4
4
  gemspec
5
+
6
+ gem "rake", "~> 13.0"
7
+ gem "minitest", "~> 6.0"
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # mini_sanity
2
2
 
3
- In-line [sanity checks], written as extensions to core Ruby objects.
4
- See API listing below, or browse the [full documentation].
3
+ In-line [sanity checks][], written as extensions to core Ruby objects.
4
+ See API listing below, or browse the [full documentation][].
5
5
 
6
6
  [sanity checks]: https://en.wikipedia.org/wiki/Sanity_check
7
- [full documentation]: http://www.rubydoc.info/gems/mini_sanity/
7
+ [full documentation]: https://www.rubydoc.info/gems/mini_sanity/
8
8
 
9
9
 
10
10
  ## Example
@@ -13,93 +13,46 @@ See API listing below, or browse the [full documentation].
13
13
  require "json"
14
14
  require "pathname"
15
15
 
16
- path = Pathname.new("hosted_files.json").assert_exist!
16
+ path = Pathname.new("hosted_files.json").assert!(&:exist?)
17
17
 
18
- hosted_files = JSON.parse(path.read).assert_instance_of!(Array)
18
+ hosted_files = JSON.parse(path.read).assert!(Array)
19
19
 
20
20
  urls = hosted_files.flat_map do |file_info|
21
- file_info.fetch("mirror_urls").refute_empty!
21
+ file_info.fetch("mirror_urls").refute!(&:empty?)
22
22
  end
23
23
 
24
24
  domains = urls.map do |url|
25
- url.assert_match!(%r"^https?://").split("/")[2]
25
+ url.assert!(%r"^https?://").split("/")[2]
26
26
  end.uniq
27
27
  ```
28
28
 
29
29
 
30
30
  ## API
31
31
 
32
- - [Object](http://www.rubydoc.info/gems/mini_sanity/Object)
33
- - [#assert_equal!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_equal%21)
34
- - [#assert_in!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_in%21)
35
- - [#assert_instance_of!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_instance_of%21)
36
- - [#assert_kind_of!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_kind_of%21)
37
- - [#assert_nil!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_nil%21)
38
- - [#assert_respond_to!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_respond_to%21)
39
- - [#refute_equal!](http://www.rubydoc.info/gems/mini_sanity/Object:refute_equal%21)
40
- - [#refute_in!](http://www.rubydoc.info/gems/mini_sanity/Object:refute_in%21)
41
- - [#refute_nil!](http://www.rubydoc.info/gems/mini_sanity/Object:refute_nil%21)
42
- - [Array](http://www.rubydoc.info/gems/mini_sanity/Array)
43
- - [#assert_length!](http://www.rubydoc.info/gems/mini_sanity/Array:assert_length%21)
44
- - [#refute_length!](http://www.rubydoc.info/gems/mini_sanity/Array:refute_length%21)
45
- - [Enumerable](http://www.rubydoc.info/gems/mini_sanity/Enumerable)
46
- - [#assert_empty!](http://www.rubydoc.info/gems/mini_sanity/Enumerable:assert_empty%21)
47
- - [#refute_empty!](http://www.rubydoc.info/gems/mini_sanity/Enumerable:refute_empty%21)
48
- - [String](http://www.rubydoc.info/gems/mini_sanity/String)
49
- - [#assert_empty!](http://www.rubydoc.info/gems/mini_sanity/String:assert_empty%21)
50
- - [#assert_length!](http://www.rubydoc.info/gems/mini_sanity/String:assert_length%21)
51
- - [#assert_match!](http://www.rubydoc.info/gems/mini_sanity/String:assert_match%21)
52
- - [#refute_empty!](http://www.rubydoc.info/gems/mini_sanity/String:refute_empty%21)
53
- - [#refute_length!](http://www.rubydoc.info/gems/mini_sanity/String:refute_length%21)
54
- - [#refute_match!](http://www.rubydoc.info/gems/mini_sanity/String:refute_match%21)
55
- - [Pathname](http://www.rubydoc.info/gems/mini_sanity/Pathname)
56
- - [#assert_dir!](http://www.rubydoc.info/gems/mini_sanity/Pathname:assert_dir%21)
57
- - [#assert_exist!](http://www.rubydoc.info/gems/mini_sanity/Pathname:assert_exist%21)
58
- - [#assert_file!](http://www.rubydoc.info/gems/mini_sanity/Pathname:assert_file%21)
59
- - [#refute_dir!](http://www.rubydoc.info/gems/mini_sanity/Pathname:refute_dir%21)
60
- - [#refute_exist!](http://www.rubydoc.info/gems/mini_sanity/Pathname:refute_exist%21)
61
- - [#refute_file!](http://www.rubydoc.info/gems/mini_sanity/Pathname:refute_file%21)
62
-
63
-
64
- ## Util API
65
-
66
- *mini_sanity* also includes a few optional utility methods which perform
67
- some function, check an assertion on the result, and raise an error with
68
- a helpful message if the assertion fails. You must add
69
- `require "mini_sanity/util"` to your script to access these methods.
70
-
71
- - [Enumerable](http://www.rubydoc.info/gems/mini_sanity/Enumerable)
72
- - [#first!](http://www.rubydoc.info/gems/mini_sanity/Enumerable:first%21)
73
- - [Regexp](http://www.rubydoc.info/gems/mini_sanity/Regexp)
74
- - [#match!](http://www.rubydoc.info/gems/mini_sanity/Regexp:match%21)
75
- - [String](http://www.rubydoc.info/gems/mini_sanity/String)
76
- - [#change](http://www.rubydoc.info/gems/mini_sanity/String:change)
77
- - [#change!](http://www.rubydoc.info/gems/mini_sanity/String:change%21)
78
- - [#match!](http://www.rubydoc.info/gems/mini_sanity/String:match%21)
32
+ - `"mini_sanity/assert"`
33
+ - [`Object#assert!`](https://www.rubydoc.info/gems/mini_sanity/Object:assert%21)
34
+ - [`Object#refute!`](https://www.rubydoc.info/gems/mini_sanity/Object:refute%21)
35
+ - `"mini_sanity/change"`
36
+ - [`String#change`](https://www.rubydoc.info/gems/mini_sanity/String:change)
37
+ - [`String#change!`](https://www.rubydoc.info/gems/mini_sanity/String:change%21)
38
+ - `"mini_sanity/match"`
39
+ - [`Regexp#match!`](https://www.rubydoc.info/gems/mini_sanity/Regexp:match%21)
40
+ - [`String#match!`](https://www.rubydoc.info/gems/mini_sanity/String:match%21)
41
+ - `"mini_sanity/results"`
42
+ - [`Enumerator#result!`](https://www.rubydoc.info/gems/mini_sanity/Enumerator:result%21)
43
+ - [`Enumerator#results!`](https://www.rubydoc.info/gems/mini_sanity/Enumerator:results%21)
79
44
 
80
45
 
81
46
  ## Installation
82
47
 
83
- Install from [Ruby Gems](https://rubygems.org/gems/mini_sanity):
84
-
85
- ```bash
86
- $ gem install mini_sanity
87
- ```
88
-
89
- Then require in your Ruby script:
90
-
91
- ```ruby
92
- require "mini_sanity"
93
- require "mini_sanity/util" # OPTIONAL
94
- ```
48
+ Install the [`mini_sanity` gem](https://rubygems.org/gems/mini_sanity).
95
49
 
96
50
 
97
51
  ## Contributing
98
52
 
99
- Run `rake test` to run the tests. You can also run `rake irb` for an
100
- interactive prompt that pre-loads the project code.
53
+ Run `rake test` to run the tests.
101
54
 
102
55
 
103
56
  ## License
104
57
 
105
- [MIT License](https://opensource.org/licenses/MIT)
58
+ [MIT License](LICENSE.txt)
data/Rakefile CHANGED
@@ -1,19 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
- require "yard"
4
-
5
-
6
- YARD::Rake::YardocTask.new(:doc) do |t|
7
- end
8
-
9
- desc "Launch IRB with this gem pre-loaded"
10
- task :irb do
11
- require "mini_sanity"
12
- require "mini_sanity/util"
13
- require "irb"
14
- ARGV.clear
15
- IRB.start
16
- end
17
3
 
18
4
  Rake::TestTask.new(:test) do |t|
19
5
  t.libs << "test"
@@ -0,0 +1,163 @@
1
+ require_relative "error"
2
+
3
+
4
+ module MiniSanity
5
+ # Matches truthy values (i.e. not +false+ or +nil+).
6
+ TRUTHY = ->(x){ !!x }
7
+
8
+ def TRUTHY.inspect
9
+ "MiniSanity::TRUTHY"
10
+ end
11
+ end
12
+
13
+
14
+ class Object
15
+
16
+ # Checks that a given +pattern+ matches the Object (or a derivative
17
+ # value), and returns the Object. Raises an exception if the pattern
18
+ # does not match.
19
+ #
20
+ # If a block is given, the Object is yielded to the block, and the
21
+ # derivative value returned by the block is checked instead.
22
+ #
23
+ # @example Match truthy
24
+ # "foo".assert! # == "foo"
25
+ # nil.assert! # raises exception
26
+ #
27
+ # @example Match expected value
28
+ # "foo".assert!("foo") # == "foo"
29
+ # "bar".assert!("foo") # raises exception
30
+ #
31
+ # @example Match Set of permitted values
32
+ # "foo".assert!(Set["foo", "bar"]) # == "foo"
33
+ # "bar".assert!(Set["foo", "bar"]) # == "bar"
34
+ # "baz".assert!(Set["foo", "bar"]) # raises exception
35
+ #
36
+ # @example Match Class
37
+ # 25.assert!(Integer) # == 25
38
+ # 2.5.assert!(Integer) # raises exception
39
+ #
40
+ # @example Match Regexp
41
+ # "foo".assert!(/^f/) # == "foo"
42
+ # "bar".assert!(/^f/) # raises exception
43
+ #
44
+ # @example Match Range
45
+ # 2.assert!(1..4) # == 2
46
+ # 5.assert!(1..4) # raises exception
47
+ #
48
+ # @example Match truthy derivative value
49
+ # [2, 5].assert!(&:any?) # == [2, 5]
50
+ # [nil].assert!(&:any?) # raises exception
51
+ #
52
+ # @example Match derivative value
53
+ # [2, 5].assert!(1.., &:length) # == [2, 5]
54
+ # [].assert!(1.., &:length) # raises exception
55
+ #
56
+ # @overload assert!(pattern = MiniSanity::TRUTHY, hint: nil)
57
+ # @param pattern [#===]
58
+ # @param hint [String]
59
+ # Hint to include in the error message
60
+ # @return [self]
61
+ # @raise [MiniSanity::Error]
62
+ # if +pattern+ does not match the Object
63
+ #
64
+ # @overload assert!(pattern = MiniSanity::TRUTHY, hint: nil, &block)
65
+ # @param pattern [#===]
66
+ # @param hint [String]
67
+ # Hint to include in the error message
68
+ # @yieldparam itself [self]
69
+ # @yieldreturn [Object]
70
+ # Derivative value
71
+ # @return [self]
72
+ # @raise [MiniSanity::Error]
73
+ # if +pattern+ does not match the value returned by +block+
74
+ def assert!(pattern = MiniSanity::TRUTHY, hint: nil, &block)
75
+ result = block ? block.call(self) : self
76
+
77
+ unless pattern === result
78
+ raise MiniSanity::Error.new("Assert failed", {
79
+ "Value" => self.inspect,
80
+ "Derived value (from #{MiniSanity::Error.describe_block(&block) || "block"})" =>
81
+ (result.inspect if block),
82
+ "Assert matches" => pattern.inspect,
83
+ "Hint" => hint,
84
+ })
85
+ end
86
+
87
+ self
88
+ end
89
+
90
+ # Checks that a given +pattern+ does not match the Object (or a
91
+ # derivative value), and returns the Object. Raises an exception if
92
+ # the pattern matches.
93
+ #
94
+ # If a block is given, the Object is yielded to the block, and the
95
+ # derivative value returned by the block is checked instead.
96
+ #
97
+ # @example Refute truthy
98
+ # false.refute! # == false
99
+ # "bad".refute! # raises exception
100
+ #
101
+ # @example Refute forbidden value
102
+ # "foo".refute!("bad") # == "foo"
103
+ # "bad".refute!("bad") # raises exception
104
+ #
105
+ # @example Refute Set of prohibited values
106
+ # "foo".refute!(Set["bad", "worse"]) # == "foo"
107
+ # "bad".refute!(Set["bad", "worse"]) # raises exception
108
+ #
109
+ # @example Refute Class
110
+ # 25.refute!(Float) # == 25
111
+ # 2.5.refute!(Float) # raises exception
112
+ #
113
+ # @example Refute Regexp
114
+ # "foo".refute!(/^ba/) # == "foo"
115
+ # "bad".refute!(/^ba/) # raises exception
116
+ #
117
+ # @example Refute Range
118
+ # 2.refute!(5..) # == 2
119
+ # 5.refute!(5..) # raises exception
120
+ #
121
+ # @example Refute truthy derivative value
122
+ # [2].refute!(&:empty?) # == [2]
123
+ # [].refute!(&:empty?) # raises exception
124
+ #
125
+ # @example Refute derivative value
126
+ # [2].refute!(1.., &:length) # == [2]
127
+ # [2, 5].refute!(1.., &:length) # raises exception
128
+ #
129
+ # @overload refute!(pattern = MiniSanity::TRUTHY, hint: nil)
130
+ # @param pattern [#===]
131
+ # @param hint [String]
132
+ # Hint to include in the error message
133
+ # @return [self]
134
+ # @raise [MiniSanity::Error]
135
+ # if +pattern+ matches the Object
136
+ #
137
+ # @overload refute!(pattern = MiniSanity::TRUTHY, hint: nil, &block)
138
+ # @param pattern [#===]
139
+ # @param hint [String]
140
+ # Hint to include in the error message
141
+ # @yieldparam itself [self]
142
+ # @yieldreturn [Object]
143
+ # Derivative value
144
+ # @return [self]
145
+ # @raise [MiniSanity::Error]
146
+ # if +pattern+ matches the value returned by +block+
147
+ def refute!(pattern = MiniSanity::TRUTHY, hint: nil, &block)
148
+ result = block ? block.call(self) : self
149
+
150
+ if pattern === result
151
+ raise MiniSanity::Error.new("Refute failed", {
152
+ "Value" => self.inspect,
153
+ "Derived value (from #{MiniSanity::Error.describe_block(&block) || "block"})" =>
154
+ (result.inspect if block),
155
+ "Refute matches" => pattern.inspect,
156
+ "Hint" => hint,
157
+ })
158
+ end
159
+
160
+ self
161
+ end
162
+
163
+ end
@@ -0,0 +1,107 @@
1
+ require_relative "error"
2
+
3
+
4
+ class String
5
+
6
+ # Like {https://docs.ruby-lang.org/en/master/String.html#method-i-sub-21
7
+ # +String#sub!+}, but raises an exception if no substitution is
8
+ # performed.
9
+ #
10
+ # @example
11
+ # info = "library: LIB_NAME\nlanguage: LIB_LANG\n"
12
+ # info.change!(/\bLIB_NAME\b/, "mini_sanity") # == "library: mini_sanity\nlanguage: LIB_LANG\n"
13
+ # info # == "library: mini_sanity\nlanguage: LIB_LANG\n"
14
+ # info.change!(/\bLIB_LANGUAGE\b/, "Ruby") # raises exception
15
+ #
16
+ # @overload change!(pattern, replacement)
17
+ # @param pattern [Regexp, String]
18
+ # Pattern to search for
19
+ # @param replacement [String]
20
+ # Replacement String (see +String#sub+ documentation for more
21
+ # information)
22
+ # @return [String]
23
+ # @raise [MiniSanity::Error]
24
+ # if no substitution was performed
25
+ #
26
+ # @overload change!(pattern, hash)
27
+ # @param pattern [Regexp, String]
28
+ # Pattern to search for
29
+ # @param hash [Hash]
30
+ # Substitution Hash (see +String#sub+ documentation for more
31
+ # information)
32
+ # @return [String]
33
+ # @raise [MiniSanity::Error]
34
+ # if no substitution was performed
35
+ #
36
+ # @overload change!(pattern, &block)
37
+ # @param pattern [Regexp, String]
38
+ # Pattern to search for
39
+ # @yieldparam match [String]
40
+ # Matched String
41
+ # @yieldreturn [String]
42
+ # Replacement String
43
+ # @return [String]
44
+ # @raise [MiniSanity::Error]
45
+ # if no substitution was performed
46
+ def change!(pattern, replacement = nil, &block)
47
+ result = if replacement
48
+ self.sub!(pattern, replacement)
49
+ else
50
+ self.sub!(pattern, &block)
51
+ end
52
+
53
+ if !result
54
+ raise MiniSanity::Error.new("String does not match pattern", {
55
+ "String" => self.inspect,
56
+ "Pattern" => pattern.inspect,
57
+ })
58
+ end
59
+
60
+ result
61
+ end
62
+
63
+ # Like {https://docs.ruby-lang.org/en/master/String.html#method-i-sub
64
+ # +String#sub+}, but raises an exception if no substitution is
65
+ # performed.
66
+ #
67
+ # @example
68
+ # info = "library: LIB_NAME\nlanguage: LIB_LANG\n"
69
+ # info.change(/\bLIB_NAME\b/, "mini_sanity") # == "library: mini_sanity\nlanguage: LIB_LANG\n"
70
+ # info # == "library: LIB_NAME\nlanguage: LIB_LANG\n"
71
+ # info.change(/\bLIB_LANGUAGE\b/, "Ruby") # raises exception
72
+ #
73
+ # @overload change(pattern, replacement)
74
+ # @param pattern [Regexp, String]
75
+ # Pattern to search for
76
+ # @param replacement [String]
77
+ # Replacement String (see +String#sub+ documentation for more
78
+ # information)
79
+ # @return [String]
80
+ # @raise [MiniSanity::Error]
81
+ # if no substitution was performed
82
+ #
83
+ # @overload change(pattern, hash)
84
+ # @param pattern [Regexp, String]
85
+ # Pattern to search for
86
+ # @param hash [Hash]
87
+ # Substitution Hash (see +String#sub+ documentation for more
88
+ # information)
89
+ # @return [String]
90
+ # @raise [MiniSanity::Error]
91
+ # if no substitution was performed
92
+ #
93
+ # @overload change(pattern, &block)
94
+ # @param pattern [Regexp, String]
95
+ # Pattern to search for
96
+ # @yieldparam match [String]
97
+ # Matched String
98
+ # @yieldreturn [String]
99
+ # Replacement String
100
+ # @return [String]
101
+ # @raise [MiniSanity::Error]
102
+ # if no substitution was performed
103
+ def change(pattern, replacement = nil, &block)
104
+ self.dup.change!(pattern, replacement, &block)
105
+ end
106
+
107
+ end
@@ -1,11 +1,21 @@
1
- class MiniSanity::Error < RuntimeError
1
+ module MiniSanity
2
+ class Error < RuntimeError
2
3
 
3
- def initialize(name, expected, actual)
4
- super(
5
- "\n#{name || "value"} violates expectations\n" \
6
- " expected: #{expected}\n" \
7
- " actual: #{actual}\n"
8
- )
9
- end
4
+ def initialize(message, details = {})
5
+ super([
6
+ message,
7
+ *details.compact.map{|name, value| "#{name}:\n #{value}" }
8
+ ].join("\n\n"))
9
+ end
10
+
11
+ # @!visibility private
12
+ def self.describe_block(&block)
13
+ if (symbol_name = block.to_s[/\(&:(.+)\)>$/, 1])
14
+ "&:#{symbol_name}"
15
+ elsif block&.source_location
16
+ "block@#{block.source_location.join(":")}"
17
+ end
18
+ end
10
19
 
20
+ end
11
21
  end
@@ -0,0 +1,57 @@
1
+ require_relative "error"
2
+
3
+
4
+ class String
5
+
6
+ # Like {https://docs.ruby-lang.org/en/master/String.html#method-i-match
7
+ # +String#match+}, but raises an exception if the match fails.
8
+ #
9
+ # @example
10
+ # "user@example.com".match!(/^([^@]+)@(.+)$/) # === MatchData
11
+ # "@user".match!(/^([^@]+)@(.+)$/) # raises exception
12
+ #
13
+ # @param pattern [Regexp]
14
+ # Pattern to search for
15
+ # @param pos [Integer]
16
+ # Position in the String to search from
17
+ # @return [MatchData]
18
+ # @raise [MiniSanity::Error]
19
+ # if +pattern+ does not match the String
20
+ def match!(pattern, pos = 0)
21
+ result = self.match(pattern, pos)
22
+
23
+ if result.nil?
24
+ raise MiniSanity::Error.new("String does not match pattern", {
25
+ "String" => self.inspect,
26
+ "Relevant portion (from position #{pos})" => (self[pos..].inspect if pos != 0),
27
+ "Pattern" => pattern.inspect,
28
+ })
29
+ end
30
+
31
+ result
32
+ end
33
+
34
+ end
35
+
36
+
37
+ class Regexp
38
+
39
+ # Like {https://docs.ruby-lang.org/en/master/Regexp.html#method-i-match
40
+ # +Regexp#match+}, but raises an exception if the match fails.
41
+ #
42
+ # @example
43
+ # /^([^@]+)@(.+)$/.match!("user@example.com") # === MatchData
44
+ # /^([^@]+)@(.+)$/.match!("@user") # raises exception
45
+ #
46
+ # @param str [String]
47
+ # String to search
48
+ # @param pos [Integer]
49
+ # Position in +str+ to search from
50
+ # @return [MatchData]
51
+ # @raise [MiniSanity::Error]
52
+ # if the Regexp does not match +str+
53
+ def match!(str, pos = 0)
54
+ str.match!(self, pos)
55
+ end
56
+
57
+ end