Skip to content

Commit

Permalink
Initial implementation of bound and unbound methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Tikhonov committed Mar 16, 2015
1 parent cc0479b commit 7bc25ef
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 9 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Regexy is the ruby gem that contains a lot of common-use regular expressions (su
- [Installation](#installation)
- [Usage](#usage)
* [General usage](#regexyregexp)
* [Getting the original regexp](#getting-the-original-regexp)
* [Combining expressions](#combining-regular-expressions)
* [Bound and unbound methods](#bound-and-unbound-regular-expressions)
* [Email addresses](#regexywebemail)
* [Hashtag](#regexywebhashtag)
* [IP addresses](#regexywebipv4)
Expand Down Expand Up @@ -49,6 +51,13 @@ r4 = Regexy::Regexp.new('foo', Regexp::IGNORECASE) # pass additional configurati
'abcfoocde' =~ r1 # => 3
r2.match 'abcfoocde' # => #<MatchData "foo">
```
### Getting the original regexp
For methods, that checks if it's arguments `is_a` Regexp instances (for example `String#scan`) you can use `internal_regexp` method.
```ruby
str = 'Email me at first@mail.com or second@mail.com'
str.scan(Regexy::Web::Email.new.unbound.internal_regexp).map(&:first) # => ["first@mail.com", "second@mail.com"]
```

### Combining regular expressions

You can combine your regular expressions with `|` operator using `|` method (or `or`, which is alias for it). Note, that regexp options will be combined too.
Expand All @@ -65,7 +74,17 @@ Regexy::Regexp.new(/foo\z/i) + /bar/ # => /foobar/i
Regexy::Regexp.new(/foo/).and_then '\Abar' # => /foobar/
Regexy::Regexp.new(/\Afoo\z/).and_then '\Abar\z' # => /\Afoobar\z/
```
### Bound and unbound regular expressions
All build-in regular expressions provided in a form of `\A...\z`, which means that they match entire string only. You can remove or add string boundaries using `bound` and `unbound` methods.
Optional arguments `method` available (`:both` by default) - `:left` for manipulating only leading `\A` and `:right` for trailing `\z`.
```ruby
Regexy::Regexp.new('/Afoo/z').unbound(:left) # => /foo\z/
Regexy::Regexp.new(/foo/i).bound # => /\Afoo\z/i

# Example - find all ip addresses in the string
str = '0.0.0.0 and 255.255.255.255 are both valid ip addresses'
str.scan(Regexy::Web::IPv4.new.unbound.internal_regexp).flatten # => ["0.0.0.0", "255.255.255.255"]
```
### Regexy::Web::Email

Generates regular expressions for email addresses validation (with unicode support). Available options: `:relaxed` for general sanity check, `:normal` (which is default) with some additional length and ip addresses validations and `:strict` for the paranoids.
Expand Down
24 changes: 24 additions & 0 deletions lib/regexy/regexp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@ def + other

alias_method :and_then, :+

def bound(method = :both)
new_regexp = source
method = method.to_sym
if method == :left || method == :both
new_regexp.prepend('\A')
end
if method == :right || method == :both
new_regexp.concat('\z')
end
::Regexy::Regexp.new(new_regexp, options)
end

def unbound(method = :both)
new_regexp = source
method = method.to_sym
if method == :left || method == :both
new_regexp.sub!(/\A\\A/, '')
end
if method == :right || method == :both
new_regexp.sub!(/\\z\s*\z/, '')
end
::Regexy::Regexp.new(new_regexp, options)
end

protected

def normalize_regexp(regexp, *args)
Expand Down
6 changes: 3 additions & 3 deletions lib/regexy/web/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
module Regexy
module Web
class Email < ::Regexy::RegexpWithMode
EMAIL_RELAXED = /\A\s*[^@\s]+@([^@\s]+\.)+[^@\s]+\s*\z/i.freeze
EMAIL_NORMAL = /\A\s*([^@\s]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,})\s*\z/i.freeze
EMAIL_STRICT = /\A\s*([-\p{L}\d+._]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,})\s*\z/i.freeze
EMAIL_RELAXED = /\A\s*(([^@\s]+)@(([^@\s]+\.)+[^@\s]+))\s*\z/i.freeze
EMAIL_NORMAL = /\A\s*(([^@\s]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,}))\s*\z/i.freeze
EMAIL_STRICT = /\A\s*(([-\p{L}\d+._]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,}))\s*\z/i.freeze

protected

Expand Down
2 changes: 1 addition & 1 deletion lib/regexy/web/hashtag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Regexy
module Web
class Hashtag < ::Regexy::Regexp
HASHTAG = /\A#(?=.{2,140}\z)([0-9_\p{L}]*[_\p{L}][0-9_\p{L}]*)\z/u.freeze
HASHTAG = /\A(#(?=.{2,140}\z)([0-9_\p{L}]*[_\p{L}][0-9_\p{L}]*))\z/ui.freeze

def initialize(*args)
super(HASHTAG, *args)
Expand Down
6 changes: 3 additions & 3 deletions lib/regexy/web/ip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Web
PORT = /:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])\z/i.freeze

class IPv4 < ::Regexy::RegexpWithMode
IPV4_NORMAL = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/i.freeze
IPV4_NORMAL = /\A((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))\z/i.freeze
IPV4_WITH_PORT = (::Regexy::Regexp.new(IPV4_NORMAL) + PORT.source).freeze

protected
Expand All @@ -18,13 +18,13 @@ def regexp_for(mode)
end

class IPv6 < ::Regexy::RegexpWithMode
IPV6_NORMAL = /\A(?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}(?:[0-9]{1,3}\.){3}
IPV6_NORMAL = /\A((?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}(?:[0-9]{1,3}\.){3}
[0-9]{1,3}(?![:.\w]))(([0-9A-F]{1,4}:){0,5}|:)((:[0-9A-F]{1,4}){1,5}:|:)
|::(?:[A-F0-9]{1,4}:){5})(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|
[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|
(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}
[A-F0-9]{0,4}(?![:.\w]))(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}
|:)|(?:[A-F0-9]{1,4}:){7}:|:(:[A-F0-9]{1,4}){7})(?![:.\w])\z
|:)|(?:[A-F0-9]{1,4}:){7}:|:(:[A-F0-9]{1,4}){7})(?![:.\w]))\z
/ix.freeze

IPV6_WITH_PORT = (::Regexy::Regexp.new(/\A\[/) + IPV6_NORMAL + /\]/ + PORT.source).freeze
Expand Down
4 changes: 2 additions & 2 deletions lib/regexy/web/url.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
module Regexy
module Web
class Url < ::Regexy::Regexp
URL = /\A([a-z][a-z\d+\-.]*:(\/\/([\p{L}\d\-._~%!$&'()*+,;=]+@)?([\p{L}\d\-._~%]+|
URL = /\A(([a-z][a-z\d+\-.]*:(\/\/([\p{L}\d\-._~%!$&'()*+,;=]+@)?([\p{L}\d\-._~%]+|
\[[\p{L}\d:.]+\]|\[v[a-f0-9][\p{L}\d\-._~%!$&'()*+,;=:]+\])(:[0-9]+)?
(\/[\p{L}\d\-._~%!$&'()*+,;=:@]+)*\/?|(\/?[\p{L}\d\-._~%!$&'()*+,;=:@]+
(\/[\p{L}\d\-._~%!$&'()*+,;=:@]+)*\/?)?)|([\p{L}\d\-._~%!$&'()*+,;=@]+
(\/[\p{L}\d\-._~%!$&'()*+,;=:@]+)*\/?|(\/[\p{L}\d\-._~%!$&'()*+,;=:@]+)
+\/?))
(\?[\p{L}\d\-._~%!$&'()*+,;=:@\/?]*)?(\#[\p{L}\d\-._~%!$&'()*+,;=:@\/?]*)?\z
(\?[\p{L}\d\-._~%!$&'()*+,;=:@\/?]*)?(\#[\p{L}\d\-._~%!$&'()*+,;=:@\/?]*)?)\z
/ix.freeze

def initialize(*args)
Expand Down
36 changes: 36 additions & 0 deletions spec/regexp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@
expect(r1 + r2).to eq /\A\z/
end
end

context '#bound' do
it 'prepends \A to regexp when method is :left' do
expect(Regexy::Regexp.new(/foo/).bound(:left)).to eq /\Afoo/
end

it 'appends \z when method is :right' do
expect(Regexy::Regexp.new(/foo/).bound(:right)).to eq /foo\z/
end

it 'prepends \A and appends \z when method is :both' do
expect(Regexy::Regexp.new(/foo/).bound(:both)).to eq /\Afoo\z/
end

it 'preserves regexp options' do
expect(Regexy::Regexp.new(/foo/i).bound).to eq /\Afoo\z/i
end
end

context '#unbound' do
it 'removes leading \A when method is :left' do
expect(Regexy::Regexp.new(/\Afoo/).unbound(:left)).to eq /foo/
end

it 'removes trailing \z when method is :right' do
expect(Regexy::Regexp.new(/foo\z/).unbound(:right)).to eq /foo/
end

it 'removes \A and \z when method is :both' do
expect(Regexy::Regexp.new(/\Afoo\z/).unbound(:both)).to eq /foo/
end

it 'preserves regexp options' do
expect(Regexy::Regexp.new(/\Afoo\z/i).unbound).to eq /foo/i
end
end
end

describe Regexy::RegexpWithMode do
Expand Down

0 comments on commit 7bc25ef

Please sign in to comment.