grex_ruby 0.2.0 → 0.3.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
  SHA256:
3
- metadata.gz: 9eea98be32e92c0d87bc6675840278c2472767a7f57e48d01fde8bedb59b650d
4
- data.tar.gz: 7bd0abf1026dbe6f9bb4e6c764952301c948da7b6647b9a2015b3a7553bb3e04
3
+ metadata.gz: 781efb31741fa9806bf76e010c0414b06bdd4dfaad5ef213ad4eba27d60ca976
4
+ data.tar.gz: d902f4f544a50bf54c71eb5ecbd2b743628ea195b13c282b1baef7aff54f7e69
5
5
  SHA512:
6
- metadata.gz: 25bb8ab1dd9ac55dfdfcf1eb37841be34abd390cf2d27a4f541e1fb59735b780b5f4dabc0853b8463b2464cc0d7c222dfba91afa3c1f059196c5e7c0d4607f20
7
- data.tar.gz: a45f66a23f259de711df1c84ce8e2df4335c2b568a493492cdddb4b7bce21f89fc072e29e88dff2ec2008cf43d824a6d45ebee51bf4ff0047a788823f08d66f2
6
+ metadata.gz: 06d66538fd78d54b19d8f4c79c4b57c9e50afe65dd225b866354a8b057e753703b388a5aa81003d2dbbfbc96f4dd497ef33183427abcd028f1e78a4e4e4ef554
7
+ data.tar.gz: 26d1802d580e715d89431471ea6744fad9290cc651cc8a9802b4704def599525c8bb6577f83c9e862b363773cf4bf70d6073d393e96fbe54bebe87643a119683
data/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ exe/* text eol=lf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - Add `Grex.generate_from_file` with UTF-8 input, LF/CRLF line handling and keyword options.
6
+ - Add the `grex-ruby` command with flags for every generation option.
7
+ - Add command-line help, version output and readable errors with nonzero exit status.
8
+ - Document file input and command-line usage, including local development commands.
9
+ - Add integration tests for the CLI and file-based generation.
10
+
3
11
  ## 0.2.0
4
12
 
5
13
  - Expose the public API as `Grex`, keeping `GrexRuby` as a compatibility alias.
data/Cargo.lock CHANGED
@@ -228,7 +228,7 @@ dependencies = [
228
228
 
229
229
  [[package]]
230
230
  name = "grex_ruby"
231
- version = "0.2.0"
231
+ version = "0.3.0"
232
232
  dependencies = [
233
233
  "grex",
234
234
  "magnus",
data/README.md CHANGED
@@ -401,6 +401,97 @@ An empty array raises `ArgumentError` with the message `samples must not be empt
401
401
  Passing a string instead of an array, or an array containing an integer, raises
402
402
  `TypeError`.
403
403
 
404
+ ## Reading examples from a file
405
+
406
+ Starting with 0.3.0, `Grex.generate_from_file` reads a UTF-8 file with one example
407
+ per line and accepts the same keyword options as `Grex.generate`:
408
+
409
+ ```ruby
410
+ # samples.txt contains two lines: 123 and 234.
411
+ Grex.generate_from_file('samples.txt', convert_digits: true)
412
+ # => "^\\p{Nd}\\p{Nd}\\p{Nd}$"
413
+ ```
414
+
415
+ LF and Windows CRLF line endings are removed; other whitespace is preserved.
416
+ The last line does not need a newline. An empty file raises `ArgumentError`;
417
+ a missing file raises `Errno::ENOENT`.
418
+
419
+ ## Command-line usage
420
+
421
+ Version 0.3.0 adds the `grex-ruby` command. After installing the gem:
422
+
423
+ ```text
424
+ grex-ruby --help
425
+ grex-ruby --version
426
+ grex-ruby samples.txt
427
+ ```
428
+
429
+ Given a UTF-8 `samples.txt` containing:
430
+
431
+ ```text
432
+ 123
433
+ 234
434
+ ```
435
+
436
+ Run:
437
+
438
+ ```text
439
+ grex-ruby samples.txt --convert-digits
440
+ ```
441
+
442
+ Output:
443
+
444
+ ```text
445
+ ^\p{Nd}\p{Nd}\p{Nd}$
446
+ ```
447
+
448
+ Flags may appear before or after the file path, and can be combined:
449
+
450
+ ```text
451
+ grex-ruby samples.txt --convert-digits --convert-repetitions --no-anchors
452
+ ```
453
+
454
+ Output:
455
+
456
+ ```text
457
+ \p{Nd}{3}
458
+ ```
459
+
460
+ All generation options are available:
461
+
462
+ | CLI flag | Ruby keyword |
463
+ | --- | --- |
464
+ | `--convert-digits` | `convert_digits: true` |
465
+ | `--convert-non-digits` | `convert_non_digits: true` |
466
+ | `--convert-whitespace` | `convert_whitespace: true` |
467
+ | `--convert-non-whitespace` | `convert_non_whitespace: true` |
468
+ | `--convert-words` | `convert_words: true` |
469
+ | `--convert-non-words` | `convert_non_words: true` |
470
+ | `--convert-repetitions` | `convert_repetitions: true` |
471
+ | `--minimum-repetitions N` | `minimum_repetitions: N` |
472
+ | `--minimum-substring-length N` | `minimum_substring_length: N` |
473
+ | `--no-anchors` | `anchors: false` |
474
+ | `--no-start-anchor` | `start_anchor: false` |
475
+ | `--no-end-anchor` | `end_anchor: false` |
476
+ | `-i`, `--ignore-case` | `ignore_case: true` |
477
+ | `--capturing-groups` | `capturing_groups: true` |
478
+ | `-x`, `--verbose` | `verbose: true` |
479
+ | `--escape-non-ascii` | `escape_non_ascii: true` |
480
+
481
+ Anchors are enabled by default. The positive forms `--anchors`, `--start-anchor`
482
+ and `--end-anchor` are also accepted. As in the Ruby API, `--no-anchors` overrides
483
+ the individual anchor settings. Numeric minimums must be positive 32-bit integers.
484
+
485
+ The command accepts exactly one file path. Use `--` before a path that starts
486
+ with `-`. Patterns are written to stdout; errors are written to stderr with exit
487
+ status `1`. Successful generation, `--help`, and `--version` exit with status `0`.
488
+
489
+ To run the CLI from a checkout before installing it:
490
+
491
+ ```bash
492
+ bundle exec ruby -Ilib exe/grex-ruby samples.txt --convert-digits
493
+ ```
494
+
404
495
  ## Development
405
496
 
406
497
  ### Linux
data/exe/grex-ruby ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'grex_ruby'
5
+ require 'optparse'
6
+
7
+ options = {}
8
+
9
+ parser = OptionParser.new do |opts|
10
+ opts.banner = 'Usage: grex-ruby FILE [options]'
11
+
12
+ opts.on('--convert-digits', 'Convert digits to a Unicode digit class') do
13
+ options[:convert_digits] = true
14
+ end
15
+
16
+ opts.on('--convert-non-digits', 'Convert non-digits to a Unicode character class') do
17
+ options[:convert_non_digits] = true
18
+ end
19
+
20
+ opts.on('--convert-whitespace', 'Convert whitespace to a Unicode character class') do
21
+ options[:convert_whitespace] = true
22
+ end
23
+
24
+ opts.on('--convert-non-whitespace', 'Convert non-whitespace to a Unicode character class') do
25
+ options[:convert_non_whitespace] = true
26
+ end
27
+
28
+ opts.on('--convert-words', 'Convert Unicode word characters') do
29
+ options[:convert_words] = true
30
+ end
31
+
32
+ opts.on('--convert-non-words', 'Convert characters outside the Unicode word class') do
33
+ options[:convert_non_words] = true
34
+ end
35
+
36
+ opts.on('--convert-repetitions', 'Convert repeated substrings to quantifiers') do
37
+ options[:convert_repetitions] = true
38
+ end
39
+
40
+ opts.on('--minimum-repetitions N', Integer, 'Minimum repetitions after the first occurrence (default: 1)') do |value|
41
+ options[:minimum_repetitions] = value
42
+ end
43
+
44
+ opts.on('--minimum-substring-length N', Integer, 'Minimum repeated fragment length (default: 1)') do |value|
45
+ options[:minimum_substring_length] = value
46
+ end
47
+
48
+ opts.on('--[no-]anchors', 'Keep both line anchors (default: enabled)') do |value|
49
+ options[:anchors] = value
50
+ end
51
+
52
+ opts.on('--[no-]start-anchor', 'Keep the initial line anchor (default: enabled)') do |value|
53
+ options[:start_anchor] = value
54
+ end
55
+
56
+ opts.on('--[no-]end-anchor', 'Keep the final line anchor (default: enabled)') do |value|
57
+ options[:end_anchor] = value
58
+ end
59
+
60
+ opts.on('-i', '--ignore-case', 'Match without regard to letter case') do
61
+ options[:ignore_case] = true
62
+ end
63
+
64
+ opts.on('--capturing-groups', 'Use capturing groups') do
65
+ options[:capturing_groups] = true
66
+ end
67
+
68
+ opts.on('-x', '--verbose', 'Format the pattern across multiple lines') do
69
+ options[:verbose] = true
70
+ end
71
+
72
+ opts.on('--escape-non-ascii', 'Escape non-ASCII characters as Unicode code points') do
73
+ options[:escape_non_ascii] = true
74
+ end
75
+
76
+ opts.on('-V', '--version', 'Show version') do
77
+ puts Grex::VERSION
78
+ exit
79
+ end
80
+
81
+ opts.on('-h', '--help', 'Show help') do
82
+ puts opts
83
+ exit
84
+ end
85
+ end
86
+
87
+ begin
88
+ parser.parse!
89
+ raise OptionParser::MissingArgument, 'FILE' if ARGV.empty?
90
+ raise OptionParser::InvalidArgument, 'expected exactly one FILE' unless ARGV.length == 1
91
+
92
+ puts Grex.generate_from_file(ARGV.first, **options)
93
+ rescue OptionParser::ParseError, ArgumentError, RangeError, SystemCallError, EncodingError => error
94
+ warn "grex-ruby: #{error.message}"
95
+ warn 'Run grex-ruby --help for usage.'
96
+ exit 1
97
+ end
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "grex_ruby"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  authors = ["Nelly Simkova <sk8high3r@ya.ru>"]
6
6
  publish = false
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grex
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
6
6
 
7
7
  # Compatibility with the public module name used in version 0.1.0.
data/lib/grex_ruby.rb CHANGED
@@ -1,66 +1,71 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'grex_ruby/version'
4
- require 'grex_ruby/grex_ruby'
5
-
6
- module Grex
7
- class Error < StandardError; end
8
-
9
- def self.generate(samples,
10
- convert_digits: false,
11
- convert_repetitions: false,
12
- minimum_substring_length: 1,
13
- minimum_repetitions: 1,
14
- anchors: true,
15
- ignore_case: false,
16
- capturing_groups: false,
17
- verbose: false,
18
- convert_non_digits: false,
19
- convert_whitespace: false,
20
- convert_non_whitespace: false,
21
- convert_words: false,
22
- convert_non_words: false,
23
- start_anchor: true,
24
- end_anchor: true,
25
- escape_non_ascii: false)
26
- pattern = generate_native(samples,
27
- { convert_digits:, convert_repetitions:, minimum_substring_length:, minimum_repetitions:,
28
- anchors:, ignore_case:, capturing_groups:, verbose:, convert_non_digits:, convert_whitespace:, convert_non_whitespace:, convert_words:, convert_non_words:, start_anchor:, end_anchor:, escape_non_ascii: })
29
- adapt_pattern(pattern)
30
- end
31
-
32
- def self.adapt_pattern(pattern)
33
- chars = pattern.chars
34
- result = +''
35
- index = 0
36
-
37
- while index < chars.length
38
- if chars[index] == '\\'
39
- result << case chars[index + 1]
40
- when 'd'
41
- '\p{Nd}'
42
- when 'D'
43
- '\P{Nd}'
44
- when 's'
45
- '\p{Space}'
46
- when 'S'
47
- '\P{Space}'
48
- when 'w'
49
- '[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
50
- when 'W'
51
- '[^\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
52
- else
53
- chars[index, 2].join
54
- end
55
- index += 2
56
- else
57
- result << chars[index]
58
- index += 1
59
- end
60
- end
61
-
62
- result
63
- end
64
-
65
- private_class_method :generate_native, :adapt_pattern
66
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'grex_ruby/version'
4
+ require 'grex_ruby/grex_ruby'
5
+
6
+ module Grex
7
+ class Error < StandardError; end
8
+
9
+ def self.generate(samples,
10
+ convert_digits: false,
11
+ convert_repetitions: false,
12
+ minimum_substring_length: 1,
13
+ minimum_repetitions: 1,
14
+ anchors: true,
15
+ ignore_case: false,
16
+ capturing_groups: false,
17
+ verbose: false,
18
+ convert_non_digits: false,
19
+ convert_whitespace: false,
20
+ convert_non_whitespace: false,
21
+ convert_words: false,
22
+ convert_non_words: false,
23
+ start_anchor: true,
24
+ end_anchor: true,
25
+ escape_non_ascii: false)
26
+ pattern = generate_native(samples,
27
+ { convert_digits:, convert_repetitions:, minimum_substring_length:, minimum_repetitions:,
28
+ anchors:, ignore_case:, capturing_groups:, verbose:, convert_non_digits:, convert_whitespace:, convert_non_whitespace:, convert_words:, convert_non_words:, start_anchor:, end_anchor:, escape_non_ascii: })
29
+ adapt_pattern(pattern)
30
+ end
31
+
32
+ def self.adapt_pattern(pattern)
33
+ chars = pattern.chars
34
+ result = +''
35
+ index = 0
36
+
37
+ while index < chars.length
38
+ if chars[index] == '\\'
39
+ result << case chars[index + 1]
40
+ when 'd'
41
+ '\p{Nd}'
42
+ when 'D'
43
+ '\P{Nd}'
44
+ when 's'
45
+ '\p{Space}'
46
+ when 'S'
47
+ '\P{Space}'
48
+ when 'w'
49
+ '[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
50
+ when 'W'
51
+ '[^\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_Control}]'
52
+ else
53
+ chars[index, 2].join
54
+ end
55
+ index += 2
56
+ else
57
+ result << chars[index]
58
+ index += 1
59
+ end
60
+ end
61
+
62
+ result
63
+ end
64
+
65
+ def self.generate_from_file(path, **options)
66
+ lines = File.readlines(path, encoding: 'UTF-8', chomp: true)
67
+ generate(lines, **options)
68
+ end
69
+
70
+ private_class_method :generate_native, :adapt_pattern
71
+ end
data/sig/grex_ruby.rbs CHANGED
@@ -20,6 +20,26 @@ module Grex
20
20
  ?end_anchor: bool,
21
21
  ?escape_non_ascii: bool,
22
22
  ) -> String
23
+
24
+ def self.generate_from_file: (
25
+ String,
26
+ ?convert_digits: bool,
27
+ ?convert_repetitions: bool,
28
+ ?minimum_substring_length: Integer,
29
+ ?minimum_repetitions: Integer,
30
+ ?anchors: bool,
31
+ ?ignore_case: bool,
32
+ ?capturing_groups: bool,
33
+ ?verbose: bool,
34
+ ?convert_non_digits: bool,
35
+ ?convert_whitespace: bool,
36
+ ?convert_non_whitespace: bool,
37
+ ?convert_words: bool,
38
+ ?convert_non_words: bool,
39
+ ?start_anchor: bool,
40
+ ?end_anchor: bool,
41
+ ?escape_non_ascii: bool,
42
+ ) -> String
23
43
  end
24
44
 
25
45
  module GrexRuby = Grex
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grex_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nelly Simkova
@@ -23,13 +23,35 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: 0.9.128
26
+ - !ruby/object:Gem::Dependency
27
+ name: optparse
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.3'
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0.3'
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '1.0'
26
46
  email:
27
47
  - sk8high3r@ya.ru
28
- executables: []
48
+ executables:
49
+ - grex-ruby
29
50
  extensions:
30
51
  - ext/grex_ruby/extconf.rb
31
52
  extra_rdoc_files: []
32
53
  files:
54
+ - ".gitattributes"
33
55
  - ".github/workflows/ci.yml"
34
56
  - CHANGELOG.md
35
57
  - Cargo.lock
@@ -38,6 +60,7 @@ files:
38
60
  - README.md
39
61
  - Rakefile
40
62
  - THIRD_PARTY_NOTICES.md
63
+ - exe/grex-ruby
41
64
  - ext/grex_ruby/Cargo.toml
42
65
  - ext/grex_ruby/build.rs
43
66
  - ext/grex_ruby/extconf.rb