mini_sanity 1.0.0 → 1.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
  SHA1:
3
- metadata.gz: c5da04d9d5a007087a446a04332d0ffc6384a5d8
4
- data.tar.gz: a26754f66a63338980c31d97461d8a443d868106
3
+ metadata.gz: ba5fd068770843f3044c6dade58282429e1330e5
4
+ data.tar.gz: 1aa0bfd6c7f88f9a0e1d992cd18289c5a9328ac1
5
5
  SHA512:
6
- metadata.gz: d595fc9b17a60fb29ece9f2d553c0549aaa341ce055d16589157e4be2be072b0c28d92c8ea3a35973d6bce0aaf30444b1644f6f3704033e39462eb3289c9d8e7
7
- data.tar.gz: '08db65b91fc28caa65d47a711afb0cb338ad0edfd1a601e78e642bb1846f4e150547612b42f996e77222c9ac27c8a35a86fa437da97a7c12fbc8c7af4cadb005'
6
+ metadata.gz: 1dfe7f149c334cd7ea7eb2330ddf834b72a17bb3009cdd45f42dd8e1347278084a65c835a1d7d438e881a64d42750ab8fda3eefd0fb498a919e76faa38a59bd3
7
+ data.tar.gz: ea9642cd25487cf2f54d990843f79ba98cece1b560811ea791644e2c010707d910fb5def8800a9518bec04ef274b9db7bf9884a1612549888ee6dfe5102f7260
@@ -0,0 +1,32 @@
1
+ ## 1.1.0
2
+
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!
28
+
29
+
30
+ ## 1.0.0
31
+
32
+ * Initial release
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # mini_sanity
2
2
 
3
- In-line [sanity checks], written as extensions to core Ruby objects. See
4
- 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
7
  [full documentation]: http://www.rubydoc.info/gems/mini_sanity/
8
8
 
9
9
 
10
- # Example
10
+ ## Example
11
11
 
12
12
  ```ruby
13
13
  require "json"
@@ -27,22 +27,55 @@ end.uniq
27
27
  ```
28
28
 
29
29
 
30
- # API
30
+ ## API
31
31
 
32
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)
33
35
  - [#assert_instance_of!](http://www.rubydoc.info/gems/mini_sanity/Object:assert_instance_of%21)
34
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)
35
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)
36
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)
37
45
  - [Enumerable](http://www.rubydoc.info/gems/mini_sanity/Enumerable)
46
+ - [#assert_empty!](http://www.rubydoc.info/gems/mini_sanity/Enumerable:assert_empty%21)
38
47
  - [#refute_empty!](http://www.rubydoc.info/gems/mini_sanity/Enumerable:refute_empty%21)
39
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)
40
51
  - [#assert_match!](http://www.rubydoc.info/gems/mini_sanity/String:assert_match%21)
41
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)
42
54
  - [#refute_match!](http://www.rubydoc.info/gems/mini_sanity/String:refute_match%21)
43
55
  - [Pathname](http://www.rubydoc.info/gems/mini_sanity/Pathname)
56
+ - [#assert_dir!](http://www.rubydoc.info/gems/mini_sanity/Pathname:assert_dir%21)
44
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)
45
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)
46
79
 
47
80
 
48
81
  ## Installation
@@ -57,6 +90,7 @@ Then require in your Ruby script:
57
90
 
58
91
  ```ruby
59
92
  require "mini_sanity"
93
+ require "mini_sanity/util" # OPTIONAL
60
94
  ```
61
95
 
62
96
 
data/Rakefile CHANGED
@@ -9,6 +9,7 @@ end
9
9
  desc "Launch IRB with this gem pre-loaded"
10
10
  task :irb do
11
11
  require "mini_sanity"
12
+ require "mini_sanity/util"
12
13
  require "irb"
13
14
  ARGV.clear
14
15
  IRB.start
@@ -1,5 +1,6 @@
1
1
  require_relative "mini_sanity/version"
2
2
  require_relative "mini_sanity/error"
3
+ require_relative "mini_sanity/array"
3
4
  require_relative "mini_sanity/enumerable"
4
5
  require_relative "mini_sanity/object"
5
6
  require_relative "mini_sanity/pathname"
@@ -0,0 +1,63 @@
1
+ class Array
2
+
3
+ # Checks that the Array matches a given length, and returns the Array
4
+ # unmodified. If the Array fails this check, an exception is
5
+ # raised.
6
+ #
7
+ # @example
8
+ # coord = [0, 0, 0]
9
+ # coord.assert_length(3)! # == [0, 0, 0]
10
+ #
11
+ # coord = [0, 0]
12
+ # coord.assert_length(3)! # raises exception
13
+ #
14
+ # coord = [0, 0]
15
+ # coord.assert_length(2..3)! # == [0, 0]
16
+ #
17
+ # @param length [Integer, Range<Integer>]
18
+ # length to match
19
+ # @param name [String, Symbol]
20
+ # optional name to include in the error message
21
+ # @return [self]
22
+ # @raise [MiniSanity::Error]
23
+ # if the Array length does not match +length+
24
+ def assert_length!(length, name = nil)
25
+ if !(length === self.length)
26
+ raise MiniSanity::Error.new(name,
27
+ "#{self.class} having #{length} elements",
28
+ self.inspect)
29
+ end
30
+ self
31
+ end
32
+
33
+ # Checks that the Array does not match a given length, and returns the
34
+ # Array unmodified. If the Array fails this check, an exception is
35
+ # raised.
36
+ #
37
+ # @example
38
+ # some = ["one"]
39
+ # some.refute_length!(0) # == ["one"]
40
+ #
41
+ # some = []
42
+ # some.refute_length(0)! # raises exception
43
+ #
44
+ # many = ["one", "many"]
45
+ # many.refute_length!(0..1) # == ["one", "many"]
46
+ #
47
+ # @param length [Integer, Range<Integer>]
48
+ # length to not match
49
+ # @param name [String, Symbol]
50
+ # optional name to include in the error message
51
+ # @return [self]
52
+ # @raise [MiniSanity::Error]
53
+ # if the Array length matches +length+
54
+ def refute_length!(length, name = nil)
55
+ if length === self.length
56
+ raise MiniSanity::Error.new(name,
57
+ "#{self.class} not having #{length} elements",
58
+ self.inspect)
59
+ end
60
+ self
61
+ end
62
+
63
+ end
@@ -1,12 +1,37 @@
1
1
  module Enumerable
2
2
 
3
+ # Checks that the Enumerable is empty, and returns the Enumerable
4
+ # unmodified. If the Enumerable fails this check, an exception is
5
+ # raised.
6
+ #
7
+ # @example
8
+ # errors = []
9
+ # errors.assert_empty! # == []
10
+ #
11
+ # errors = ["something went wrong"]
12
+ # errors.assert_empty! # raises exception
13
+ #
14
+ # @param name [String, Symbol]
15
+ # optional name to include in the error message
16
+ # @return [self]
17
+ # @raise [MiniSanity::Error]
18
+ # if the Enumerable is not empty
19
+ def assert_empty!(name = nil)
20
+ if self.any?{ true }
21
+ raise MiniSanity::Error.new(name,
22
+ "empty #{self.class}",
23
+ self.inspect)
24
+ end
25
+ self
26
+ end
27
+
3
28
  # Checks that the Enumerable is not empty, and returns the Enumerable
4
29
  # unmodified. If the Enumerable fails this check, an exception is
5
30
  # raised.
6
31
  #
7
32
  # @example
8
- # [7, 8].refute_empty! # == [7, 8]
9
- # [].refute_empty! # raises exception
33
+ # ["result 1"].refute_empty! # == ["result 1"]
34
+ # [].refute_empty! # raises exception
10
35
  #
11
36
  # @param name [String, Symbol]
12
37
  # optional name to include in the error message
@@ -14,8 +39,12 @@ module Enumerable
14
39
  # @raise [MiniSanity::Error]
15
40
  # if the Enumerable is empty
16
41
  def refute_empty!(name = nil)
17
- if self.empty?
18
- raise MiniSanity::Error.new("#{name || self.class} is empty")
42
+ # NOTE use #any? instead of #none? because Array#none? seems to be
43
+ # significantly slower than Array#any? (and likewise for Hash)
44
+ if !self.any?{ true }
45
+ raise MiniSanity::Error.new(name,
46
+ "non-empty #{self.class}",
47
+ self.inspect)
19
48
  end
20
49
  self
21
50
  end
@@ -1,2 +1,11 @@
1
1
  class MiniSanity::Error < RuntimeError
2
+
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
10
+
2
11
  end
@@ -1,11 +1,35 @@
1
1
  class Object
2
2
 
3
+ # Checks that the Object is nil, and returns nil. If the Object fails
4
+ # this check, an exception is raised.
5
+ #
6
+ # @example
7
+ # result = {}
8
+ # result[:error].assert_nil! # == nil
9
+ #
10
+ # result = { error: "something went wrong" }
11
+ # result[:error].assert_nil! # == raises exception
12
+ #
13
+ # @param name [String, Symbol]
14
+ # optional name to include in the error message
15
+ # @return [self]
16
+ # @raise [MiniSanity::Error]
17
+ # if the Object is not nil
18
+ def assert_nil!(name = nil)
19
+ if !self.nil?
20
+ raise MiniSanity::Error.new(name,
21
+ "nil",
22
+ self.inspect)
23
+ end
24
+ self
25
+ end
26
+
3
27
  # Checks that the Object is not nil, and returns the Object
4
28
  # unmodified. If the Object fails this check, an exception is raised.
5
29
  #
6
30
  # @example
7
- # [7, 8].first.refute_nil! # == 7
8
- # [].first.refute_nil! # raises exception
31
+ # ["result 1"].first.refute_nil! # == "result 1"
32
+ # [].first.refute_nil! # raises exception
9
33
  #
10
34
  # @param name [String, Symbol]
11
35
  # optional name to include in the error message
@@ -14,8 +38,105 @@ class Object
14
38
  # if the Object is nil
15
39
  def refute_nil!(name = nil)
16
40
  if self.nil?
17
- message = name ? "#{name} is nil" : "unexpected nil"
18
- raise MiniSanity::Error.new(message)
41
+ raise MiniSanity::Error.new(name,
42
+ "non-nil value",
43
+ "nil")
44
+ end
45
+ self
46
+ end
47
+
48
+ # Checks that the Object equals a given expected value, and returns
49
+ # the Object unmodified. If the Object fails this check, an exception
50
+ # is raised.
51
+ #
52
+ # @example
53
+ # "good".assert_equal!("good") # == "good"
54
+ # "bad".assert_equal!("good") # raises exception
55
+ #
56
+ # @param expect [Object]
57
+ # value to expect
58
+ # @param name [String, Symbol]
59
+ # optional name to include in the error message
60
+ # @return [self]
61
+ # @raise [MiniSanity::Error]
62
+ # if the Object does not equal +expect+
63
+ def assert_equal!(expect, name = nil)
64
+ if self != expect
65
+ raise MiniSanity::Error.new(name,
66
+ expect.inspect,
67
+ self.inspect)
68
+ end
69
+ self
70
+ end
71
+
72
+ # Checks that the Object does not equal a given reject value, and
73
+ # returns the Object unmodified. If the Object fails this check, an
74
+ # exception is raised.
75
+ #
76
+ # @example
77
+ # "good".refute_equal!("bad") # == "good"
78
+ # "bad".refute_equal!("bad") # raises exception
79
+ #
80
+ # @param reject [Object]
81
+ # value to reject
82
+ # @param name [String, Symbol]
83
+ # optional name to include in the error message
84
+ # @return [self]
85
+ # @raise [MiniSanity::Error]
86
+ # if the Object equals +reject+
87
+ def refute_equal!(reject, name = nil)
88
+ if self == reject
89
+ raise MiniSanity::Error.new(name,
90
+ "not #{reject.inspect}",
91
+ self.inspect)
92
+ end
93
+ self
94
+ end
95
+
96
+ # Checks that the Object is included in a given collection, and
97
+ # returns the Object unmodified. If the Object fails this check, an
98
+ # exception is raised.
99
+ #
100
+ # @example
101
+ # "good".assert_in!(["ok", "good", "great"]) # == "good"
102
+ # "bad".assert_in!(["ok", "good", "great"]) # raises exception
103
+ #
104
+ # @param permitted [Enumerable, #include?]
105
+ # collection of permitted values
106
+ # @param name [String, Symbol]
107
+ # optional name to include in the error message
108
+ # @return [self]
109
+ # @raise [MiniSanity::Error]
110
+ # if the Object is not included in +permitted+
111
+ def assert_in!(permitted, name = nil)
112
+ if !permitted.include?(self)
113
+ raise MiniSanity::Error.new(name,
114
+ "value included in #{permitted.inspect}",
115
+ self.inspect)
116
+ end
117
+ self
118
+ end
119
+
120
+ # Checks that the Object is not included in a given collection, and
121
+ # returns the Object unmodified. If the Object fails this check, an
122
+ # exception is raised.
123
+ #
124
+ # @example
125
+ # "good".refute_in!(["bad", "poor", "fail"]) # == "good"
126
+ # "bad".refute_in!(["bad", "poor", "fail"]) # raises exception
127
+ #
128
+ # @param prohibited [Enumerable, #include?]
129
+ # collection of prohibited values
130
+ # @param name [String, Symbol]
131
+ # optional name to include in the error message
132
+ # @return [self]
133
+ # @raise [MiniSanity::Error]
134
+ # if the Object is included in +prohibited+
135
+ def refute_in!(prohibited, name = nil)
136
+ if prohibited.include?(self)
137
+ raise MiniSanity::Error.new(name,
138
+ "value not included in #{prohibited.inspect}",
139
+ self.inspect)
19
140
  end
20
141
  self
21
142
  end
@@ -25,8 +146,8 @@ class Object
25
146
  # is raised.
26
147
  #
27
148
  # @example
28
- # "abc".assert_instance_of!(String) # == "abc"
29
- # "abc".assert_instance_of!(Numeric) # raises exception
149
+ # 123.assert_instance_of!(Numeric) # == 123
150
+ # 123.assert_instance_of!(String) # raises exception
30
151
  #
31
152
  # @param klass [Class]
32
153
  # class to check
@@ -36,9 +157,10 @@ class Object
36
157
  # @raise [MiniSanity::Error]
37
158
  # if the Object is not an instance of +klass+
38
159
  def assert_instance_of!(klass, name = nil)
39
- unless self.instance_of?(klass)
40
- prelude = name ? "#{name} is instance of #{self.class}" : "unexpected #{self.class}"
41
- raise MiniSanity::Error.new("#{prelude}; expected #{klass}")
160
+ if !self.instance_of?(klass)
161
+ raise MiniSanity::Error.new(name,
162
+ "instance of #{klass}",
163
+ "instance of #{self.class}: #{self.inspect}")
42
164
  end
43
165
  self
44
166
  end
@@ -48,8 +170,8 @@ class Object
48
170
  # this check, an exception is raised.
49
171
  #
50
172
  # @example
51
- # 42.assert_kind_of!(Numeric) # == 42
52
- # 42.assert_kind_of!(Float) # raises exception
173
+ # 123.assert_kind_of!(Numeric) # == 123
174
+ # 123.assert_kind_of!(Float) # raises exception
53
175
  #
54
176
  # @param klass [Class]
55
177
  # class to check
@@ -59,9 +181,10 @@ class Object
59
181
  # @raise [MiniSanity::Error]
60
182
  # if the Object is not an instance of +klass+ or its subclasses
61
183
  def assert_kind_of!(klass, name = nil)
62
- unless self.kind_of?(klass)
63
- prelude = name ? "#{name} is instance of #{self.class}" : "unexpected #{self.class}"
64
- raise MiniSanity::Error.new("#{prelude}; expected #{klass} or one of its subclasses")
184
+ if !self.kind_of?(klass)
185
+ raise MiniSanity::Error.new(name,
186
+ "instance of #{klass} or one of its subclasses",
187
+ "instance of #{self.class}: #{self.inspect}")
65
188
  end
66
189
  self
67
190
  end
@@ -82,8 +205,10 @@ class Object
82
205
  # @raise [MiniSanity::Error]
83
206
  # if the Object does not respond to +method_name+
84
207
  def assert_respond_to!(method_name, name = nil)
85
- unless self.respond_to?(method_name)
86
- raise MiniSanity::Error.new("#{name || self.class} does not respond to #{method_name}")
208
+ if !self.respond_to?(method_name)
209
+ raise MiniSanity::Error.new(name,
210
+ "object responding to method #{method_name}",
211
+ "instance of #{self.class}: #{self.inspect}")
87
212
  end
88
213
  self
89
214
  end
@@ -1,6 +1,6 @@
1
1
  class Pathname
2
2
 
3
- # Checks that the file or directory indicated by the Pathname exists,
3
+ # Checks that the Pathname represents an existing file or directory,
4
4
  # and returns the Pathname unmodified. If the Pathname fails this
5
5
  # check, an exception is raised.
6
6
  #
@@ -12,18 +12,19 @@ class Pathname
12
12
  # optional name to include in the error message
13
13
  # @return [self]
14
14
  # @raise [MiniSanity::Error]
15
- # if the file or directory indicated by the Pathname does not exist
15
+ # if the Pathname does not represent an existing file or directory
16
16
  def assert_exist!(name = nil)
17
- unless self.exist?
18
- descriptor = name ? "#{name} (#{self})" : self.to_s
19
- raise MiniSanity::Error.new("#{descriptor} does not exist")
17
+ if !self.exist?
18
+ raise MiniSanity::Error.new(name,
19
+ "existent file or directory",
20
+ "#{self} does not exist")
20
21
  end
21
22
  self
22
23
  end
23
24
 
24
- # Checks that the file or directory indicated by the Pathname does
25
- # not already exist, and returns the Pathname unmodified. If the
26
- # Pathname fails this check, an exception is raised.
25
+ # Checks that the Pathname does not represent an existing file or
26
+ # directory, and returns the Pathname unmodified. If the Pathname
27
+ # fails this check, an exception is raised.
27
28
  #
28
29
  # @example
29
30
  # Pathname.new("/dev/null/nope").refute_exist! # == Pathname.new("/dev/null/nope")
@@ -33,11 +34,100 @@ class Pathname
33
34
  # optional name to include in the error message
34
35
  # @return [self]
35
36
  # @raise [MiniSanity::Error]
36
- # if the file or directory indicated by the Pathname already exists
37
+ # if the Pathname represents an existing file or directory
37
38
  def refute_exist!(name = nil)
38
39
  if self.exist?
39
- descriptor = name ? "#{name} (#{self})" : self.to_s
40
- raise MiniSanity::Error.new("#{descriptor} already exists")
40
+ raise MiniSanity::Error.new(name,
41
+ "non-existent file or directory",
42
+ "#{self} already exists")
43
+ end
44
+ self
45
+ end
46
+
47
+ # Checks that the Pathname represents an existing directory, and
48
+ # returns the Pathname unmodified. If the Pathname fails this check,
49
+ # an exception is raised.
50
+ #
51
+ # @example
52
+ # Pathname.new(__dir__).assert_dir! # == Pathname.new(__dir__)
53
+ # Pathname.new(__FILE__).assert_dir! # == raises exception
54
+ #
55
+ # @param name [String, Symbol]
56
+ # optional name to include in the error message
57
+ # @return [self]
58
+ # @raise [MiniSanity::Error]
59
+ # if the Pathname does not represent an existing directory
60
+ def assert_dir!(name = nil)
61
+ if !self.directory?
62
+ raise MiniSanity::Error.new(name,
63
+ "existent directory",
64
+ "#{self} is not a directory")
65
+ end
66
+ self
67
+ end
68
+
69
+ # Checks that the Pathname does not represent an existing directory,
70
+ # and returns the Pathname unmodified. If the Pathname fails this
71
+ # check, an exception is raised.
72
+ #
73
+ # @example
74
+ # Pathname.new(__FILE__).refute_dir! # == Pathname.new(__FILE__)
75
+ # Pathname.new(__dir__).refute_dir! # raises exception
76
+ #
77
+ # @param name [String, Symbol]
78
+ # optional name to include in the error message
79
+ # @return [self]
80
+ # @raise [MiniSanity::Error]
81
+ # if the Pathname represents an existing directory
82
+ def refute_dir!(name = nil)
83
+ if self.directory?
84
+ raise MiniSanity::Error.new(name,
85
+ "not an existent directory",
86
+ "#{self} is a directory")
87
+ end
88
+ self
89
+ end
90
+
91
+ # Checks that the Pathname represents an existing file, and returns
92
+ # the Pathname unmodified. If the Pathname fails this check, an
93
+ # exception is raised.
94
+ #
95
+ # @example
96
+ # Pathname.new(__FILE__).assert_file! # == Pathname.new(__FILE__)
97
+ # Pathname.new(__dir__).assert_file! # == raises exception
98
+ #
99
+ # @param name [String, Symbol]
100
+ # optional name to include in the error message
101
+ # @return [self]
102
+ # @raise [MiniSanity::Error]
103
+ # if the Pathname does not represent an existing file
104
+ def assert_file!(name = nil)
105
+ if !self.file?
106
+ raise MiniSanity::Error.new(name,
107
+ "existent file",
108
+ "#{self} is not a file")
109
+ end
110
+ self
111
+ end
112
+
113
+ # Checks that the Pathname does not represent an existing file, and
114
+ # returns the Pathname unmodified. If the Pathname fails this check,
115
+ # an exception is raised.
116
+ #
117
+ # @example
118
+ # Pathname.new(__dir__).refute_file! # == Pathname.new(__dir__)
119
+ # Pathname.new(__FILE__).refute_file! # raises exception
120
+ #
121
+ # @param name [String, Symbol]
122
+ # optional name to include in the error message
123
+ # @return [self]
124
+ # @raise [MiniSanity::Error]
125
+ # if the Pathname represents an existing file
126
+ def refute_file!(name = nil)
127
+ if self.file?
128
+ raise MiniSanity::Error.new(name,
129
+ "not an existent file",
130
+ "#{self} is a file")
41
131
  end
42
132
  self
43
133
  end
@@ -1,11 +1,32 @@
1
1
  class String
2
2
 
3
+ # Checks that the String is empty, and returns the String unmodified.
4
+ # If the String fails this check, an exception is raised.
5
+ #
6
+ # @example
7
+ # "".assert_empty! # == ""
8
+ # "bad".assert_empty! # raises exception
9
+ #
10
+ # @param name [String, Symbol]
11
+ # optional name to include in the error message
12
+ # @return [self]
13
+ # @raise [MiniSanity::Error]
14
+ # if the String is not empty
15
+ def assert_empty!(name = nil)
16
+ if !self.empty?
17
+ raise MiniSanity::Error.new(name,
18
+ "empty #{self.class}",
19
+ self.inspect)
20
+ end
21
+ self
22
+ end
23
+
3
24
  # Checks that the String is not empty, and returns the String
4
25
  # unmodified. If the String fails this check, an exception is raised.
5
26
  #
6
27
  # @example
7
- # "abc".refute_empty! # == "abc"
8
- # "".refute_empty! # raises exception
28
+ # "result".refute_empty! # == "result"
29
+ # "".refute_empty! # raises exception
9
30
  #
10
31
  # @param name [String, Symbol]
11
32
  # optional name to include in the error message
@@ -14,7 +35,59 @@ class String
14
35
  # if the String is empty
15
36
  def refute_empty!(name = nil)
16
37
  if self.empty?
17
- raise MiniSanity::Error.new("#{name || self.class} is empty")
38
+ raise MiniSanity::Error.new(name,
39
+ "non-empty #{self.class}",
40
+ self.inspect)
41
+ end
42
+ self
43
+ end
44
+
45
+ # Checks that the String matches a given length, and returns the
46
+ # String unmodified. If the String fails this check, an exception is
47
+ # raised.
48
+ #
49
+ # @example
50
+ # "password".assert_length!(8) # == "password"
51
+ # "long password".assert_length!(8..64) # == "long password"
52
+ # "pass".assert_length!(8..64) # == raises exception
53
+ #
54
+ # @param length [Integer, Range<Integer>]
55
+ # length to match
56
+ # @param name [String, Symbol]
57
+ # optional name to include in the error message
58
+ # @return [self]
59
+ # @raise [MiniSanity::Error]
60
+ # if the String length does not match +length+
61
+ def assert_length!(length, name = nil)
62
+ if !(length === self.length)
63
+ raise MiniSanity::Error.new(name,
64
+ "#{self.class} having #{length} characters",
65
+ self.inspect)
66
+ end
67
+ self
68
+ end
69
+
70
+ # Checks that the String does not match a given length, and returns
71
+ # the String unmodified. If the String fails this check, an exception
72
+ # is raised.
73
+ #
74
+ # @example
75
+ # "password".refute_length!(0) # == "password"
76
+ # "long password".refute_length!(0...8) # == "long password"
77
+ # "pass".refute_length!(0...8) # == raises exception
78
+ #
79
+ # @param length [Integer, Range<Integer>]
80
+ # length to not match
81
+ # @param name [String, Symbol]
82
+ # optional name to include in the error message
83
+ # @return [self]
84
+ # @raise [MiniSanity::Error]
85
+ # if the String length matches +length+
86
+ def refute_length!(length, name = nil)
87
+ if length === self.length
88
+ raise MiniSanity::Error.new(name,
89
+ "#{self.class} not having #{length} characters",
90
+ self.inspect)
18
91
  end
19
92
  self
20
93
  end
@@ -24,8 +97,8 @@ class String
24
97
  # exception is raised.
25
98
  #
26
99
  # @example
27
- # "abc".assert_match!(/b/) # == "abc"
28
- # "abc".assert_match!(/x/) # raises exception
100
+ # "good result".assert_match!(/^good/) # == "good result"
101
+ # "bad result".assert_match!(/^good/) # raises exception
29
102
  #
30
103
  # @param regexp [Regexp]
31
104
  # regular expression to check
@@ -35,9 +108,10 @@ class String
35
108
  # @raise [MiniSanity::Error]
36
109
  # if the String does not match +regexp+
37
110
  def assert_match!(regexp, name = nil)
38
- unless regexp =~ self
39
- descriptor = name ? "#{name} (#{self.inspect})" : self.inspect
40
- raise MiniSanity::Error.new("#{descriptor} does not match #{regexp.inspect}")
111
+ if regexp !~ self
112
+ raise MiniSanity::Error.new(name,
113
+ "#{self.class} matching #{regexp.inspect}",
114
+ self.inspect)
41
115
  end
42
116
  self
43
117
  end
@@ -47,8 +121,8 @@ class String
47
121
  # an exception is raised.
48
122
  #
49
123
  # @example
50
- # "abc".refute_match!(/x/) # == "abc"
51
- # "abc".refute_match!(/b/) # raises exception
124
+ # "good result".refute_match!(/^bad/) # == "good result"
125
+ # "bad result".refute_match!(/^bad/) # raises exception
52
126
  #
53
127
  # @param regexp [Regexp]
54
128
  # regular expression to check
@@ -59,8 +133,9 @@ class String
59
133
  # if the String matches +regexp+
60
134
  def refute_match!(regexp, name = nil)
61
135
  if regexp =~ self
62
- descriptor = name ? "#{name} (#{self.inspect})" : self.inspect
63
- raise MiniSanity::Error.new("#{descriptor} matches #{regexp.inspect}; expected no match")
136
+ raise MiniSanity::Error.new(name,
137
+ "#{self.class} not matching #{regexp.inspect}",
138
+ self.inspect)
64
139
  end
65
140
  self
66
141
  end
@@ -0,0 +1,3 @@
1
+ require_relative "util/enumerable"
2
+ require_relative "util/regexp"
3
+ require_relative "util/string"
@@ -0,0 +1,31 @@
1
+ module Enumerable
2
+
3
+ # Like {https://ruby-doc.org/core/Enumerable.html#method-i-first
4
+ # +Enumerable#first+}, but raises an exception if the Enumerable does
5
+ # not contain the requested number of elements.
6
+ #
7
+ # @example
8
+ # [7, 8, 9].first # == 7
9
+ # [7, 8, 9].first(1) # == [7]
10
+ # [7, 8, 9].first(2) # == [7, 8]
11
+ # [7, 8, 9].first(4) # raises exception
12
+ # [].first # raises exception
13
+ #
14
+ # @param n [Integer]
15
+ # requested number of elements
16
+ # @return [Object, Array]
17
+ # @raise [MiniSanity::Error]
18
+ # if the Enumerable does not contain the requested number of elements
19
+ def first!(n = nil)
20
+ result = n.nil? ? self.first : self.first(n)
21
+
22
+ if (result.nil? && !self.any?{ true }) || (!n.nil? && result.length < n)
23
+ raise MiniSanity::Error.new(nil,
24
+ "Enumerable having at least #{n || 1} elements",
25
+ self.inspect)
26
+ end
27
+
28
+ result
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ class Regexp
2
+
3
+ # Like {https://ruby-doc.org/core/Regexp.html#method-i-match
4
+ # +Regexp#match+}, but raises an exception if the match fails.
5
+ #
6
+ # @example
7
+ # /^([^@]+)@(.+)$/.match!("user@example.com") # === MatchData
8
+ # /^([^@]+)@(.+)$/.match!("@user") # raises exception
9
+ #
10
+ # @param str [String]
11
+ # string to search
12
+ # @param pos [Integer]
13
+ # position in +str+ to begin the search
14
+ # @return [MatchData]
15
+ # @raise [MiniSanity::Error]
16
+ # if the Regexp does not match +str+
17
+ def match!(str, pos = 0)
18
+ result = self.match(str, pos)
19
+ if result.nil?
20
+ raise MiniSanity::Error.new(nil,
21
+ "String matching #{self.inspect}#{" from position #{pos}" if pos != 0}",
22
+ str.inspect)
23
+ end
24
+ result
25
+ end
26
+
27
+ end
@@ -0,0 +1,77 @@
1
+ class String
2
+
3
+ # Like {https://ruby-doc.org/core/String.html#method-i-match
4
+ # +String#match+}, but raises an exception if the match fails.
5
+ #
6
+ # @example
7
+ # "user@example.com".match!(/^([^@]+)@(.+)$/) # === MatchData
8
+ # "@user".match!(/^([^@]+)@(.+)$/) # raises exception
9
+ #
10
+ # @param pattern [Regexp]
11
+ # pattern to search for
12
+ # @param pos [Integer]
13
+ # position in the String to begin the search
14
+ # @return [MatchData]
15
+ # @raise [MiniSanity::Error]
16
+ # if +pattern+ does not match the String
17
+ def match!(pattern, pos = 0)
18
+ result = self.match(pattern, pos)
19
+ if result.nil?
20
+ raise MiniSanity::Error.new(nil,
21
+ "String matching #{pattern.inspect}#{" from position #{pos}" if pos != 0}",
22
+ self.inspect)
23
+ end
24
+ result
25
+ end
26
+
27
+ # Like {https://ruby-doc.org/core/String.html#method-i-sub-21
28
+ # +String#sub!+}, but raises an exception if no substitution was
29
+ # performed.
30
+ #
31
+ # @example
32
+ # "author: YOUR_NAME".change!(/\bYOUR_NAME\b/, "Me") # == "author: Me"
33
+ # "author: TODO".change!(/\bYOUR_NAME\b/, "Me") # raises exception
34
+ #
35
+ # @param pattern [Regexp, String]
36
+ # pattern to search for
37
+ # @param replacement [String, Hash, &block]
38
+ # substitution value (see +String#sub!+ documentation for full details)
39
+ # @return [String]
40
+ # @raise [MiniSanity::Error]
41
+ # if no substitution was performed
42
+ def change!(pattern, replacement = nil, &replacement_block)
43
+ result = if replacement
44
+ self.sub!(pattern, replacement)
45
+ else
46
+ self.sub!(pattern, &replacement_block)
47
+ end
48
+
49
+ if !result
50
+ raise MiniSanity::Error.new(nil,
51
+ "String matching #{pattern.inspect}",
52
+ self.inspect)
53
+ end
54
+
55
+ result
56
+ end
57
+
58
+ # Like {https://ruby-doc.org/core/String.html#method-i-sub
59
+ # +String#sub+}, but raises an exception if no substitution was
60
+ # performed.
61
+ #
62
+ # @example
63
+ # "author: YOUR_NAME".change(/\bYOUR_NAME\b/, "Me") # == "author: Me"
64
+ # "author: TODO".change(/\bYOUR_NAME\b/, "Me") # raises exception
65
+ #
66
+ # @param pattern [Regexp, String]
67
+ # pattern to search for
68
+ # @param replacement [String, Hash, &block]
69
+ # substitution value (see +String#sub+ documentation for full details)
70
+ # @return [String]
71
+ # @raise [MiniSanity::Error]
72
+ # if no substitution was performed
73
+ def change(pattern, replacement = nil, &replacement_block)
74
+ self.dup.change!(pattern, replacement, &replacement_block)
75
+ end
76
+
77
+ end
@@ -1,3 +1,3 @@
1
1
  module MiniSanity
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_sanity
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Hefner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-26 00:00:00.000000000 Z
11
+ date: 2018-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,16 +75,22 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - ".gitignore"
77
77
  - ".travis.yml"
78
+ - CHANGELOG.md
78
79
  - Gemfile
79
80
  - LICENSE.txt
80
81
  - README.md
81
82
  - Rakefile
82
83
  - lib/mini_sanity.rb
84
+ - lib/mini_sanity/array.rb
83
85
  - lib/mini_sanity/enumerable.rb
84
86
  - lib/mini_sanity/error.rb
85
87
  - lib/mini_sanity/object.rb
86
88
  - lib/mini_sanity/pathname.rb
87
89
  - lib/mini_sanity/string.rb
90
+ - lib/mini_sanity/util.rb
91
+ - lib/mini_sanity/util/enumerable.rb
92
+ - lib/mini_sanity/util/regexp.rb
93
+ - lib/mini_sanity/util/string.rb
88
94
  - lib/mini_sanity/version.rb
89
95
  - mini_sanity.gemspec
90
96
  homepage: https://github.com/jonathanhefner/mini_sanity
@@ -107,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
113
  version: '0'
108
114
  requirements: []
109
115
  rubyforge_project:
110
- rubygems_version: 2.4.8
116
+ rubygems_version: 2.6.13
111
117
  signing_key:
112
118
  specification_version: 4
113
119
  summary: In-line sanity checks