regexp-examples 1.4.0 → 1.4.1

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: e7ea454116c6367dba39b57fb8ca5fdd6b1e5062
4
- data.tar.gz: a41c56176c0bbf007e99780790f3e196d4605190
3
+ metadata.gz: c9d287717179b54b2ee8426c8519609cc839ab06
4
+ data.tar.gz: d9f2f1c463927507aef8a199788ee136bff4d23e
5
5
  SHA512:
6
- metadata.gz: 41f307047bbd5ca1539d6eda5d4035b91649187fdb015c449c2f56a64353c601911b1da465df0e891d01fed3344e70b3bc1efbfeaa8bfd61d371b0e1a0bf5c12
7
- data.tar.gz: 2c126a5b52842b9c2b5b13fbf3243e09f6ae29d0d290561e050566bf6fa94401fa3d33a6d492bdec177ce1be9bacd49cee363ff0ce552b2986b1e0da22eea9ce
6
+ metadata.gz: 7624a0d9392ca22ee8706a96c2ea7a06fe7476602e05dd121b56609d13f5cea53a6b026b3475bd7178ad2db426d469c5813b6d47fafb5b56cb9afe5ee268fab7
7
+ data.tar.gz: 236752b70d1d56bb5534c4c6786237194d624991f8763ec5fb14639f232163494b755c881c5f4084582570258df7cc59fb8f310c5a8a845dceb61821f5837694
@@ -1,5 +1,5 @@
1
1
  Metrics/LineLength:
2
2
  Max: 90
3
3
 
4
- Style/FileName:
4
+ Naming/FileName:
5
5
  Enabled: false
@@ -1,6 +1,7 @@
1
1
  require_relative 'regexp-examples/unicode_char_ranges'
2
2
  require_relative 'regexp-examples/chargroup_parser'
3
- require_relative 'regexp-examples/constants'
3
+ require_relative 'regexp-examples/config'
4
+ require_relative 'regexp-examples/char_sets'
4
5
  require_relative 'regexp-examples/groups'
5
6
  require_relative 'regexp-examples/backreferences'
6
7
  require_relative 'regexp-examples/max_results_limiter'
@@ -0,0 +1,59 @@
1
+ # :nodoc:
2
+ module RegexpExamples
3
+ # Definitions of various special characters, used in regular expressions.
4
+ # For example, `/\h/.examples` will return the value of `Hex` in this module
5
+ module CharSets
6
+ Lower = Array('a'..'z')
7
+ Upper = Array('A'..'Z')
8
+ Digit = Array('0'..'9')
9
+ Punct = %w[! " # % & ' ( ) * , - . / : ; ? @ [ \\ ] _ { }] \
10
+ | (RUBY_VERSION >= '2.4.0' ? %w[$ + < = > ^ ` | ~] : [])
11
+ Hex = Array('a'..'f') | Array('A'..'F') | Digit
12
+ Word = Lower | Upper | Digit | ['_']
13
+ Whitespace = [' ', "\t", "\n", "\r", "\v", "\f"].freeze
14
+ Control = (0..31).map(&:chr) | ["\x7f"]
15
+ # Ensure that the "common" characters appear first in the array
16
+ # Also, ensure "\n" comes first, to make it obvious when included
17
+ Any = ["\n"] | Lower | Upper | Digit | Punct | (0..127).map(&:chr)
18
+ AnyNoNewLine = Any - ["\n"]
19
+
20
+ # Map of special regex characters, to their associated character sets
21
+ BackslashCharMap = {
22
+ 'd' => Digit,
23
+ 'D' => Any - Digit,
24
+ 'w' => Word,
25
+ 'W' => Any - Word,
26
+ 's' => Whitespace,
27
+ 'S' => Any - Whitespace,
28
+ 'h' => Hex,
29
+ 'H' => Any - Hex,
30
+
31
+ 't' => ["\t"], # tab
32
+ 'n' => ["\n"], # new line
33
+ 'r' => ["\r"], # carriage return
34
+ 'f' => ["\f"], # form feed
35
+ 'a' => ["\a"], # alarm
36
+ 'v' => ["\v"], # vertical tab
37
+ 'e' => ["\e"], # escape
38
+ }.freeze
39
+
40
+ POSIXCharMap = {
41
+ 'alnum' => Upper | Lower | Digit,
42
+ 'alpha' => Upper | Lower,
43
+ 'blank' => [' ', "\t"],
44
+ 'cntrl' => Control,
45
+ 'digit' => Digit,
46
+ 'graph' => (Any - Control) - [' '], # Visible chars
47
+ 'lower' => Lower,
48
+ 'print' => Any - Control,
49
+ 'punct' => Punct,
50
+ 'space' => Whitespace,
51
+ 'upper' => Upper,
52
+ 'xdigit' => Hex,
53
+ 'word' => Word,
54
+ 'ascii' => Any
55
+ }.freeze
56
+
57
+ NamedPropertyCharMap = UnicodeCharRanges.instance
58
+ end.freeze
59
+ end
@@ -67,7 +67,7 @@ module RegexpExamples
67
67
  end
68
68
 
69
69
  def parse_posix_group(negation_flag, name)
70
- @charset.concat negate_if(POSIXCharMap[name], !negation_flag.empty?)
70
+ @charset.concat negate_if(CharSets::POSIXCharMap[name], !negation_flag.empty?)
71
71
  @current_position += (negation_flag.length + # 0 or 1, if '^' is present
72
72
  name.length +
73
73
  2) # Length of opening and closing colons (always 2)
@@ -84,13 +84,10 @@ module RegexpExamples
84
84
  end
85
85
 
86
86
  def parse_after_backslash
87
- case next_char
88
- when 'b'
87
+ if next_char == 'b'
89
88
  ["\b"]
90
- when *BackslashCharMap.keys
91
- BackslashCharMap[next_char]
92
89
  else
93
- [next_char]
90
+ CharSets::BackslashCharMap.fetch(next_char, [next_char])
94
91
  end
95
92
  end
96
93
 
@@ -122,12 +119,11 @@ module RegexpExamples
122
119
  def parse_after_hyphen
123
120
  if regexp_string[@current_position + 1] == ']' # e.g. /[abc-]/ -- not a range!
124
121
  @charset << '-'
125
- @current_position += 1
126
122
  else
127
123
  @current_position += 1
128
124
  @charset.concat((@charset.last..parse_checking_backlash.first).to_a)
129
- @current_position += 1
130
125
  end
126
+ @current_position += 1
131
127
  end
132
128
 
133
129
  def rest_of_string
@@ -0,0 +1,64 @@
1
+ # :nodoc:
2
+ module RegexpExamples
3
+ # Configuration settings to limit the number/length of Regexp examples generated
4
+ class Config
5
+ # The maximum variance for any given repeater, to prevent a huge/infinite number of
6
+ # examples from being listed. For example, if self.max_repeater_variance = 2 then:
7
+ # .* is equivalent to .{0,2}
8
+ # .+ is equivalent to .{1,3}
9
+ # .{2,} is equivalent to .{2,4}
10
+ # .{,3} is equivalent to .{0,2}
11
+ # .{3,8} is equivalent to .{3,5}
12
+ MAX_REPEATER_VARIANCE_DEFAULT = 2
13
+
14
+ # Maximum number of characters returned from a char set, to reduce output spam
15
+ # For example, if self.max_group_results = 5 then:
16
+ # \d is equivalent to [01234]
17
+ # \w is equivalent to [abcde]
18
+ MAX_GROUP_RESULTS_DEFAULT = 5
19
+
20
+ # Maximum number of results to be generated, for Regexp#examples
21
+ # This is to prevent the system "freezing" when given instructions like:
22
+ # /[ab]{30}/.examples
23
+ # (Which would attempt to generate 2**30 == 1073741824 examples!!!)
24
+ MAX_RESULTS_LIMIT_DEFAULT = 10_000
25
+ class << self
26
+ def with_configuration(**new_config)
27
+ original_config = config.dup
28
+
29
+ begin
30
+ self.config = new_config
31
+ result = yield
32
+ ensure
33
+ self.config = original_config
34
+ end
35
+
36
+ result
37
+ end
38
+
39
+ # Thread-safe getters and setters
40
+ %i[max_repeater_variance max_group_results max_results_limit].each do |m|
41
+ define_method(m) do
42
+ config[m]
43
+ end
44
+ define_method("#{m}=") do |value|
45
+ config[m] = value
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def config=(**args)
52
+ Thread.current[:regexp_examples_config].merge!(args)
53
+ end
54
+
55
+ def config
56
+ Thread.current[:regexp_examples_config] ||= {
57
+ max_repeater_variance: MAX_REPEATER_VARIANCE_DEFAULT,
58
+ max_group_results: MAX_GROUP_RESULTS_DEFAULT,
59
+ max_results_limit: MAX_RESULTS_LIMIT_DEFAULT
60
+ }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -17,11 +17,11 @@ module RegexpExamples
17
17
  end
18
18
 
19
19
  def cumulate_total(new_results_count, cumulator_method)
20
- if @results_count.zero?
21
- @results_count = new_results_count
22
- else
23
- @results_count = @results_count.public_send(cumulator_method, new_results_count)
24
- end
20
+ @results_count = if @results_count.zero?
21
+ new_results_count
22
+ else
23
+ @results_count.public_send(cumulator_method, new_results_count)
24
+ end
25
25
  end
26
26
 
27
27
  def results_allowed_from(partial_results, limiter_method)
@@ -29,7 +29,7 @@ module RegexpExamples
29
29
  end
30
30
 
31
31
  def parse
32
- repeaters = []
32
+ repeaters = [PlaceHolderGroup.new]
33
33
  until end_of_regexp
34
34
  group = parse_group(repeaters)
35
35
  return [group] if group.is_a? OrGroup
@@ -9,7 +9,7 @@ module RegexpExamples
9
9
  parse_regular_backreference_group(Regexp.last_match(1))
10
10
  elsif rest_of_string =~ /\Ak['<]([\w-]+)['>]/
11
11
  parse_named_backreference_group(Regexp.last_match(1))
12
- elsif BackslashCharMap.keys.include?(next_char)
12
+ elsif CharSets::BackslashCharMap.keys.include?(next_char)
13
13
  parse_backslash_special_char
14
14
  elsif rest_of_string =~ /\A(c|C-)(.)/
15
15
  parse_backslash_control_char(Regexp.last_match(1), Regexp.last_match(2))
@@ -60,7 +60,7 @@ module RegexpExamples
60
60
 
61
61
  def parse_backslash_special_char
62
62
  CharGroup.new(
63
- BackslashCharMap[next_char].dup,
63
+ CharSets::BackslashCharMap[next_char].dup,
64
64
  @ignorecase
65
65
  )
66
66
  end
@@ -97,7 +97,7 @@ module RegexpExamples
97
97
  # Beware of double negatives! E.g. /\P{^Space}/
98
98
  is_negative = (p_negation == 'P') ^ (caret_negation == '^')
99
99
  CharGroup.new(
100
- negate_if(NamedPropertyCharMap[property_name.downcase], is_negative),
100
+ negate_if(CharSets::NamedPropertyCharMap[property_name.downcase], is_negative),
101
101
  @ignorecase
102
102
  )
103
103
  end
@@ -80,13 +80,13 @@ module RegexpExamples
80
80
  def initialize(group, min, has_comma, max)
81
81
  super(group)
82
82
  @min_repeats = min || 0
83
- @max_repeats = if !has_comma
84
- @min_repeats
85
- else
83
+ @max_repeats = if has_comma # e.g. a{1,}, a{,3} or a{1,3}
86
84
  [
87
85
  max,
88
86
  @min_repeats + RegexpExamples::Config.max_repeater_variance
89
87
  ].compact.min
88
+ else # e.g. a{1}
89
+ @min_repeats
90
90
  end
91
91
  end
92
92
  end
@@ -1,4 +1,5 @@
1
1
  require 'pstore'
2
+ require 'singleton'
2
3
 
3
4
  module RegexpExamples
4
5
  # Interface to the retrieve the character sets that match a regex named property.
@@ -6,6 +7,7 @@ module RegexpExamples
6
7
  # These matching values are stored, compressed, in a PStore. They are specific to
7
8
  # the ruby minor version.
8
9
  class UnicodeCharRanges
10
+ include Singleton
9
11
  # These values were generated by: scripts/unicode_lister.rb
10
12
  # Note: Only the first 128 results are listed, for performance.
11
13
  # Also, some groups seem to have no matches (weird!)
@@ -1,4 +1,4 @@
1
1
  # Gem version
2
2
  module RegexpExamples
3
- VERSION = '1.4.0'.freeze
3
+ VERSION = '1.4.1'.freeze
4
4
  end
@@ -1,18 +1,17 @@
1
1
  RSpec.describe RegexpExamples::Config do
2
-
3
2
  describe 'max_repeater_variance' do
4
3
  context 'as a passed parameter' do
5
4
  it 'with low limit' do
6
5
  expect(/[A-Z]/.examples(max_results_limit: 5))
7
- .to match_array %w(A B C D E)
6
+ .to match_array %w[A B C D E]
8
7
  end
9
8
  it 'with (default) high limit' do
10
9
  expect(/[ab]{14}/.examples.length)
11
- .to be <= 10000 # NOT 2**14 == 16384, because it's been limited
10
+ .to be <= 10_000 # NOT 2**14 == 16384, because it's been limited
12
11
  end
13
12
  it 'with (custom) high limit' do
14
- expect(/[ab]{14}/.examples(max_results_limit: 20000).length)
15
- .to eq 16384 # NOT 10000, because it's below the limit
13
+ expect(/[ab]{14}/.examples(max_results_limit: 20_000).length)
14
+ .to eq 16_384 # NOT 10000, because it's below the limit
16
15
  end
17
16
  it 'for boolean or groups' do
18
17
  expect(/[ab]{3}|[cd]{3}/.examples(max_results_limit: 10).length)
@@ -47,7 +46,7 @@ RSpec.describe RegexpExamples::Config do
47
46
 
48
47
  it 'sets limit without passing explicitly' do
49
48
  expect(/[A-Z]/.examples)
50
- .to match_array %w(A B C D E)
49
+ .to match_array %w[A B C D E]
51
50
  end
52
51
  end
53
52
  end # describe 'max_results_limit'
@@ -56,11 +55,11 @@ RSpec.describe RegexpExamples::Config do
56
55
  context 'as a passed parameter' do
57
56
  it 'with a larger value' do
58
57
  expect(/a+/.examples(max_repeater_variance: 5))
59
- .to match_array %w(a aa aaa aaaa aaaaa aaaaaa)
58
+ .to match_array %w[a aa aaa aaaa aaaaa aaaaaa]
60
59
  end
61
60
  it 'with a lower value' do
62
61
  expect(/a{4,8}/.examples(max_repeater_variance: 0))
63
- .to eq %w(aaaa)
62
+ .to eq %w[aaaa]
64
63
  end
65
64
  end
66
65
 
@@ -75,7 +74,7 @@ RSpec.describe RegexpExamples::Config do
75
74
 
76
75
  it 'sets limit without passing explicitly' do
77
76
  expect(/a+/.examples)
78
- .to match_array %w(a aa aaa aaaa aaaaa aaaaaa)
77
+ .to match_array %w[a aa aaa aaaa aaaaa aaaaaa]
79
78
  end
80
79
  end
81
80
  end # describe 'max_repeater_variance'
@@ -84,11 +83,11 @@ RSpec.describe RegexpExamples::Config do
84
83
  context 'as a passed parameter' do
85
84
  it 'with a larger value' do
86
85
  expect(/\d/.examples(max_group_results: 10))
87
- .to match_array %w(0 1 2 3 4 5 6 7 8 9)
86
+ .to match_array %w[0 1 2 3 4 5 6 7 8 9]
88
87
  end
89
88
  it 'with a lower value' do
90
89
  expect(/\d/.examples(max_group_results: 3))
91
- .to match_array %w(0 1 2)
90
+ .to match_array %w[0 1 2]
92
91
  end
93
92
  end
94
93
 
@@ -103,7 +102,7 @@ RSpec.describe RegexpExamples::Config do
103
102
 
104
103
  it 'sets limit without passing explicitly' do
105
104
  expect(/\d/.examples)
106
- .to match_array %w(0 1 2 3 4 5 6 7 8 9)
105
+ .to match_array %w[0 1 2 3 4 5 6 7 8 9]
107
106
  end
108
107
  end
109
108
  end # describe 'max_group_results'
@@ -112,24 +111,23 @@ RSpec.describe RegexpExamples::Config do
112
111
  it 'uses thread-local global config values' do
113
112
  thread = Thread.new do
114
113
  RegexpExamples::Config.max_group_results = 1
115
- expect(/\d/.examples).to eq %w(0)
114
+ expect(/\d/.examples).to eq %w[0]
116
115
  end
117
116
  sleep 0.1 # Give the above thread time to run
118
- expect(/\d/.examples).to eq %w(0 1 2 3 4)
117
+ expect(/\d/.examples).to eq %w[0 1 2 3 4]
119
118
  thread.join
120
119
  end
121
120
 
122
121
  it 'uses thread-local block config values' do
123
122
  thread = Thread.new do
124
123
  RegexpExamples::Config.with_configuration(max_group_results: 1) do
125
- expect(/\d/.examples).to eq %w(0)
124
+ expect(/\d/.examples).to eq %w[0]
126
125
  sleep 0.2 # Give the below thread time to run while this block is open
127
126
  end
128
127
  end
129
128
  sleep 0.1 # Give the above thread time to run
130
- expect(/\d/.examples).to eq %w(0 1 2 3 4)
129
+ expect(/\d/.examples).to eq %w[0 1 2 3 4]
131
130
  thread.join
132
131
  end
133
132
  end # describe 'thread safety'
134
-
135
133
  end
@@ -100,7 +100,7 @@ RSpec.describe Regexp, '#examples' do
100
100
 
101
101
  context 'for escaped characters' do
102
102
  all_letters = Array('a'..'z') | Array('A'..'Z')
103
- special_letters = %w(b c g p u x z A B C G M P Z)
103
+ special_letters = %w[b c g p u x z A B C G M P Z]
104
104
  valid_letters = all_letters - special_letters
105
105
 
106
106
  valid_letters.each do |char|
@@ -185,7 +185,7 @@ RSpec.describe Regexp, '#examples' do
185
185
  )
186
186
  # An exhaustive set of tests for all named properties!!! This is useful
187
187
  # for verifying the PStore contains correct values for all ruby versions
188
- %w(
188
+ %w[
189
189
  Alnum Alpha Blank Cntrl Digit Graph Lower Print Punct Space Upper XDigit
190
190
  Word ASCII Any Assigned L Ll Lm Lo Lt Lu M Mn Mc Me N Nd Nl No P Pc Pd
191
191
  Ps Pe Pi Pf Po S Sm Sc Sk So Z Zs Zl Zp C Cc Cf Cn Co Arabic Armenian
@@ -196,7 +196,7 @@ RSpec.describe Regexp, '#examples' do
196
196
  Mongolian Myanmar New_Tai_Lue Nko Ogham Ol_Chiki Oriya Phags_Pa Rejang
197
197
  Runic Saurashtra Sinhala Sundanese Syloti_Nagri Syriac Tagalog Tagbanwa
198
198
  Tai_Le Tamil Telugu Thaana Thai Tibetan Tifinagh Vai Yi
199
- ).each do |property|
199
+ ].each do |property|
200
200
  it "examples for /\p{#{property}}/" do
201
201
  regexp_examples = /\p{#{property}}/.examples(max_group_results: 99_999)
202
202
  expect(regexp_examples)
@@ -209,10 +209,10 @@ RSpec.describe Regexp, '#examples' do
209
209
  end
210
210
 
211
211
  # The following seem to genuinely have no matching examples (!!??!!?!)
212
- %w(
212
+ %w[
213
213
  Cs Carian Cuneiform Cypriot Deseret Gothic Kharoshthi Linear_B Lycian
214
214
  Lydian Old_Italic Old_Persian Osmanya Phoenician Shavian Ugaritic
215
- ).each do |property|
215
+ ].each do |property|
216
216
  examples_are_empty(/\p{#{property}}/)
217
217
  end
218
218
  end
@@ -261,6 +261,10 @@ RSpec.describe Regexp, '#examples' do
261
261
  )
262
262
  end
263
263
 
264
+ context 'for empty regex' do
265
+ it { expect(//.examples).to eq [''] }
266
+ end
267
+
264
268
  context 'for comment groups' do
265
269
  examples_exist_and_match(
266
270
  /a(?#comment)b/,
@@ -292,21 +296,21 @@ RSpec.describe Regexp, '#examples' do
292
296
  # More rigorous tests to assert that ALL examples are being listed
293
297
  context 'default config options' do
294
298
  # Simple examples
295
- it { expect(/[ab]{2}/.examples).to match_array %w(aa ab ba bb) }
296
- it { expect(/(a|b){2}/.examples).to match_array %w(aa ab ba bb) }
299
+ it { expect(/[ab]{2}/.examples).to match_array %w[aa ab ba bb] }
300
+ it { expect(/(a|b){2}/.examples).to match_array %w[aa ab ba bb] }
297
301
  it { expect(/a+|b?/.examples).to match_array ['a', 'aa', 'aaa', '', 'b'] }
298
302
 
299
303
  # Only display unique examples:
300
- it { expect(/a|a|b|b/.examples).to match_array %w(a b) }
301
- it { expect(/[ccdd]/.examples).to match_array %w(c d) }
304
+ it { expect(/a|a|b|b/.examples).to match_array %w[a b] }
305
+ it { expect(/[ccdd]/.examples).to match_array %w[c d] }
302
306
 
303
307
  # a{1}? should be equivalent to (?:a{1})?, i.e. NOT a "non-greedy quantifier"
304
308
  it { expect(/a{1}?/.examples).to match_array ['', 'a'] }
305
309
  end
306
310
 
307
311
  context 'end of string' do
308
- it { expect(/test\z/.examples).to match_array %w(test) }
309
- it { expect(/test\Z/.examples).to match_array ['test', "test\n"] }
312
+ it { expect(/test\z/.examples).to match_array %w[test] }
313
+ it { expect(/test\Z/.examples).to match_array %W[test test\n] }
310
314
  end
311
315
 
312
316
  context 'backreferences and escaped octal combined' do
@@ -317,12 +321,12 @@ RSpec.describe Regexp, '#examples' do
317
321
  end
318
322
 
319
323
  context 'case insensitive' do
320
- it { expect(/ab/i.examples).to match_array %w(ab aB Ab AB) }
324
+ it { expect(/ab/i.examples).to match_array %w[ab aB Ab AB] }
321
325
  it do
322
326
  expect(/a+/i.examples)
323
- .to match_array %w(a A aa aA Aa AA aaa aaA aAa aAA Aaa AaA AAa AAA)
327
+ .to match_array %w[a A aa aA Aa AA aaa aaA aAa aAA Aaa AaA AAa AAA]
324
328
  end
325
- it { expect(/([ab])\1/i.examples).to match_array %w(aa bb AA BB) }
329
+ it { expect(/([ab])\1/i.examples).to match_array %w[aa bb AA BB] }
326
330
  end
327
331
 
328
332
  context 'multiline' do
@@ -331,30 +335,30 @@ RSpec.describe Regexp, '#examples' do
331
335
  end
332
336
 
333
337
  context 'exteded form' do
334
- it { expect(/a b c/x.examples).to eq %w(abc) }
335
- it { expect(/a#comment/x.examples).to eq %w(a) }
338
+ it { expect(/a b c/x.examples).to eq %w[abc] }
339
+ it { expect(/a#comment/x.examples).to eq %w[a] }
336
340
  it do
337
341
  expect(
338
342
  /
339
343
  line1 #comment
340
344
  line2 #comment
341
345
  /x.examples
342
- ).to eq %w(line1line2)
346
+ ).to eq %w[line1line2]
343
347
  end
344
348
  end
345
349
 
346
350
  context 'options toggling' do
347
351
  context 'rest of string' do
348
- it { expect(/a(?i)b(?-i)c/.examples).to match_array %w(abc aBc) }
349
- it { expect(/a(?x) b(?-x) c/.examples).to eq %w(ab\ c) }
352
+ it { expect(/a(?i)b(?-i)c/.examples).to match_array %w[abc aBc] }
353
+ it { expect(/a(?x) b(?-x) c/.examples).to eq %w[ab\ c] }
350
354
  it { expect(/(?m)./.examples(max_group_results: 999)).to include "\n" }
351
355
  # Toggle "groups" should not increase backref group count:
352
- it { expect(/(?i)(a)-\1/.examples).to match_array %w(a-a A-A) }
356
+ it { expect(/(?i)(a)-\1/.examples).to match_array %w[a-a A-A] }
353
357
  end
354
358
  context 'subexpression' do
355
- it { expect(/a(?i:b)c/.examples).to match_array %w(abc aBc) }
356
- it { expect(/a(?i:b(?-i:c))/.examples).to match_array %w(abc aBc) }
357
- it { expect(/a(?-i:b)c/i.examples).to match_array %w(abc abC Abc AbC) }
359
+ it { expect(/a(?i:b)c/.examples).to match_array %w[abc aBc] }
360
+ it { expect(/a(?i:b(?-i:c))/.examples).to match_array %w[abc aBc] }
361
+ it { expect(/a(?-i:b)c/i.examples).to match_array %w[abc abC Abc AbC] }
358
362
  end
359
363
  end
360
364
  end # context 'exact examples match'
@@ -8,7 +8,7 @@ require 'pry'
8
8
  # Several of these tests (intentionally) use "weird" regex patterns,
9
9
  # that spam annoying warnings when running.
10
10
  # E.g. warning: invalid back reference: /\k/
11
- # and warning: character class has ']' without escape: /[]]/
11
+ # and warning: character class has ']' without escape: /[]]/
12
12
  # This config disables those warnings.
13
13
  $VERBOSE = nil
14
14
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: regexp-examples
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Lord
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-15 00:00:00.000000000 Z
11
+ date: 2017-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -60,8 +60,9 @@ files:
60
60
  - lib/core_extensions/regexp/examples.rb
61
61
  - lib/regexp-examples.rb
62
62
  - lib/regexp-examples/backreferences.rb
63
+ - lib/regexp-examples/char_sets.rb
63
64
  - lib/regexp-examples/chargroup_parser.rb
64
- - lib/regexp-examples/constants.rb
65
+ - lib/regexp-examples/config.rb
65
66
  - lib/regexp-examples/groups.rb
66
67
  - lib/regexp-examples/helpers.rb
67
68
  - lib/regexp-examples/max_results_limiter.rb
@@ -1,121 +0,0 @@
1
- # :nodoc:
2
- module RegexpExamples
3
- # Configuration settings to limit the number/length of Regexp examples generated
4
- class Config
5
- class << self
6
- def with_configuration(**new_config)
7
- original_config = config.dup
8
-
9
- begin
10
- self.config = new_config
11
- result = yield
12
- ensure
13
- self.config = original_config
14
- end
15
-
16
- result
17
- end
18
-
19
- # Thread-safe getters and setters
20
- %i[max_repeater_variance max_group_results max_results_limit].each do |m|
21
- define_method(m) do
22
- config[m]
23
- end
24
- define_method("#{m}=") do |value|
25
- config[m] = value
26
- end
27
- end
28
-
29
- private
30
-
31
- def config=(**args)
32
- Thread.current[:regexp_examples_config].merge!(args)
33
- end
34
-
35
- def config
36
- Thread.current[:regexp_examples_config] ||= {
37
- max_repeater_variance: MAX_REPEATER_VARIANCE_DEFAULT,
38
- max_group_results: MAX_GROUP_RESULTS_DEFAULT,
39
- max_results_limit: MAX_RESULTS_LIMIT_DEFAULT
40
- }
41
- end
42
- end
43
- # The maximum variance for any given repeater, to prevent a huge/infinite number of
44
- # examples from being listed. For example, if self.max_repeater_variance = 2 then:
45
- # .* is equivalent to .{0,2}
46
- # .+ is equivalent to .{1,3}
47
- # .{2,} is equivalent to .{2,4}
48
- # .{,3} is equivalent to .{0,2}
49
- # .{3,8} is equivalent to .{3,5}
50
- MAX_REPEATER_VARIANCE_DEFAULT = 2
51
-
52
- # Maximum number of characters returned from a char set, to reduce output spam
53
- # For example, if self.max_group_results = 5 then:
54
- # \d is equivalent to [01234]
55
- # \w is equivalent to [abcde]
56
- MAX_GROUP_RESULTS_DEFAULT = 5
57
-
58
- # Maximum number of results to be generated, for Regexp#examples
59
- # This is to prevent the system "freezing" when given instructions like:
60
- # /[ab]{30}/.examples
61
- # (Which would attempt to generate 2**30 == 1073741824 examples!!!)
62
- MAX_RESULTS_LIMIT_DEFAULT = 10_000
63
- end
64
-
65
- # Definitions of various special characters, used in regular expressions.
66
- # For example, `/\h/.examples` will return the value of `Hex` in this module
67
- module CharSets
68
- Lower = Array('a'..'z')
69
- Upper = Array('A'..'Z')
70
- Digit = Array('0'..'9')
71
- Punct = %w[! " # % & ' ( ) * , - . / : ; ? @ [ \\ ] _ { }] \
72
- | (RUBY_VERSION >= '2.4.0' ? %w[$ + < = > ^ ` | ~] : [])
73
- Hex = Array('a'..'f') | Array('A'..'F') | Digit
74
- Word = Lower | Upper | Digit | ['_']
75
- Whitespace = [' ', "\t", "\n", "\r", "\v", "\f"].freeze
76
- Control = (0..31).map(&:chr) | ["\x7f"]
77
- # Ensure that the "common" characters appear first in the array
78
- # Also, ensure "\n" comes first, to make it obvious when included
79
- Any = ["\n"] | Lower | Upper | Digit | Punct | (0..127).map(&:chr)
80
- AnyNoNewLine = Any - ["\n"]
81
- end.freeze
82
-
83
- # Map of special regex characters, to their associated character sets
84
- BackslashCharMap = {
85
- 'd' => CharSets::Digit,
86
- 'D' => CharSets::Any - CharSets::Digit,
87
- 'w' => CharSets::Word,
88
- 'W' => CharSets::Any - CharSets::Word,
89
- 's' => CharSets::Whitespace,
90
- 'S' => CharSets::Any - CharSets::Whitespace,
91
- 'h' => CharSets::Hex,
92
- 'H' => CharSets::Any - CharSets::Hex,
93
-
94
- 't' => ["\t"], # tab
95
- 'n' => ["\n"], # new line
96
- 'r' => ["\r"], # carriage return
97
- 'f' => ["\f"], # form feed
98
- 'a' => ["\a"], # alarm
99
- 'v' => ["\v"], # vertical tab
100
- 'e' => ["\e"], # escape
101
- }.freeze
102
-
103
- POSIXCharMap = {
104
- 'alnum' => CharSets::Upper | CharSets::Lower | CharSets::Digit,
105
- 'alpha' => CharSets::Upper | CharSets::Lower,
106
- 'blank' => [' ', "\t"],
107
- 'cntrl' => CharSets::Control,
108
- 'digit' => CharSets::Digit,
109
- 'graph' => (CharSets::Any - CharSets::Control) - [' '], # Visible chars
110
- 'lower' => CharSets::Lower,
111
- 'print' => CharSets::Any - CharSets::Control,
112
- 'punct' => CharSets::Punct,
113
- 'space' => CharSets::Whitespace,
114
- 'upper' => CharSets::Upper,
115
- 'xdigit' => CharSets::Hex,
116
- 'word' => CharSets::Word,
117
- 'ascii' => CharSets::Any
118
- }.freeze
119
-
120
- NamedPropertyCharMap = UnicodeCharRanges.new
121
- end