diff --git a/README.md b/README.md index c2e0557..becef5d 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,23 @@ TODO: fill out this section. TODO: fill out this section. +### `ShapeOf::Regexp` + +The `ShapeOf::Regexp[/regexp pattern/]` is used to match either `Regexp`s as equal or to match a `Regexp` against a `String` using `Regexp#match?`. +```ruby +shape = ShapeOf::Regexp[/foobar$/i] + +shape.shape_of?("foobar") # => true +shape.shape_of?(/foobar$/i) # => true +shape.shape_of?("fOobAr\n") # => true +shape.shape_of?("\n\nfoobar\n") # => true +shape.shape_of?("foo\nbarfoo\nfoobar\nfo\nobar\n") # => true +shape.shape_of?("There once was a barfoo who foobared. Foobar") # => true +shape.shape_of?(/foobar$/) # => false +shape.shape_of?(/foobar/i) # => false +shape.shape_of?("foo\nbar\n") # => false +``` + ### `ShapeOf::Boolean` TODO: fill out this section. diff --git a/lib/shape_of.rb b/lib/shape_of.rb index 9873ad8..019c374 100644 --- a/lib/shape_of.rb +++ b/lib/shape_of.rb @@ -316,6 +316,47 @@ def self.required? end end + class Regexp < Shape + @internal_class = ::Regexp + + def self.shape_of?(object) + object.instance_of? @internal_class + end + + def self.[](shape) + raise TypeError, "Shape must be #{::Regexp.inspect}, was #{shape.inspect}" unless shape.instance_of? ::Regexp + + Class.new(self) do + @class_name = "#{superclass.name}[#{shape.inspect}]" + @shape = shape + + def self.name + @class_name + end + + def self.to_s + @class_name + end + + def self.inspect + @class_name + end + + def self.shape_of?(object) + unless object.instance_of?(::Regexp) || object.instance_of?(String) + raise TypeError, "expected #{::Regexp.inspect} or #{String.inspect}, was instead #{object.inspect}" + end + + if object.instance_of?(::Regexp) + @shape == object + else # string + @shape.match?(object) + end + end + end + end + end + Numeric = Union[Integer, Float, Rational, Complex].tap do |this| this.instance_variable_set(:@class_name, this.name.sub(/Union.*/, 'Numeric')) end diff --git a/test/test_shape_of.rb b/test/test_shape_of.rb index 623699f..c2049ab 100644 --- a/test/test_shape_of.rb +++ b/test/test_shape_of.rb @@ -66,6 +66,11 @@ def test_nothing_is_defined assert_equal Class, ShapeOf::Nothing.class end + def test_regexp_is_defined + assert_equal "constant", defined? ShapeOf::Regexp + assert_equal Class, ShapeOf::Regexp.class + end + def test_numeric_is_defined assert_equal "constant", defined? ShapeOf::Numeric assert_equal Class, ShapeOf::Numeric.class @@ -279,6 +284,22 @@ def test_nothing_with_hash refute_shape_of_many shape, [{ foo: nil }, { foo: :bar }, { hello: "world" }, [{}]] end + # Regexp + + def test_regexp + assert_operator ShapeOf::Regexp, :respond_to?, :required? + assert_predicate ShapeOf::Regexp, :required? + assert_raises(TypeError) { ShapeOf::Regexp['foobar'] } + assert_raises(TypeError) { ShapeOf::Regexp[/foobar/].shape_of?(['hello']) } + + assert_shape_of_many ShapeOf::Regexp, [/foo/, //, /bar/, /bz/imx, Regexp.new('foobadfaejralj')] + refute_shape_of_many ShapeOf::Regexp, [nil, true, false, '', '/foobar/'] + assert_shape_of_many ShapeOf::Regexp[/foobar/], [/foobar/, 'foobar', "\n\nfoobar\n\n", /foobar/.to_s, "qwertyuiopasdfghjklzxcvbnmfoobarqwertyuioopasdfghjklzxcvbnm"] + refute_shape_of_many ShapeOf::Regexp[/foobar/], [/fobar/, 'fobar', ''] + assert_shape_of_many ShapeOf::Regexp[/^whoa/i], [/^whoa/i, 'whoa there', 'WHOA there!', "whoa hello\nwhoa there\nwhoa whoa!"] + refute_shape_of_many ShapeOf::Regexp[/^whoa/i], [/whoa/, 'hey, whoa there', " WHOA there!"] + end + # Numeric def test_numeric