will_scan_string 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +20 -0
- data/Rakefile +1 -0
- data/lib/will_scan_string/regexp_traits.rb +12 -0
- data/lib/will_scan_string/string_scanner.rb +51 -0
- data/lib/will_scan_string/version.rb +3 -0
- data/lib/will_scan_string.rb +7 -0
- data/spec/lib/will_scan_string/regexp_traits_spec.rb +41 -0
- data/spec/lib/will_scan_string/string_scanner_spec.rb +32 -0
- data/spec/spec_helper.rb +9 -0
- data/will_scan_string.gemspec +24 -0
- metadata +95 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Will scan string
|
2
|
+
================
|
3
|
+
Multi-pass string replacement has always been fun. I don't know any good way to
|
4
|
+
perform multiple replacements on strings, so I made this minor gem.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
Just don't use this, but if you're curious: Check `spec/lib/will_scan_string`
|
9
|
+
for examples and features.
|
10
|
+
|
11
|
+
Notes
|
12
|
+
-----
|
13
|
+
This gem is buggy and nowhere friendly to use. It's designed for personal use
|
14
|
+
and is very experimental. It uses various hacks to combine multiple regular
|
15
|
+
expressions to a single big-ass regular expression and it therefore is quite
|
16
|
+
common for 2 regular expressions conflicting each other.
|
17
|
+
|
18
|
+
Known regexp conflicts and issues
|
19
|
+
---------------------------------
|
20
|
+
- The gem does not support named capture groups whatsoever.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module WillScanString
|
2
|
+
module RegexpTraits
|
3
|
+
CAPTURE_GROUP_PATTERN = /(?<!\\)\((?:\?(?:<([a-z]+)\>|'([a-z]+)')|(?!\?))/i
|
4
|
+
|
5
|
+
def capture_groups
|
6
|
+
c = 0
|
7
|
+
r = []
|
8
|
+
source.scan(CAPTURE_GROUP_PATTERN) { r.push $+.present? ? $+.to_sym : c+=1 }
|
9
|
+
r
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module WillScanString
|
2
|
+
class StringScanner
|
3
|
+
def register_replacement( pattern, replacement )
|
4
|
+
@replacements = [] if @replacements.nil?
|
5
|
+
@replacements << [ pattern, replacement, @replacements.last.present? ? @replacements.last[2] + (@replacements.last[0].is_a?(Regexp) ? @replacements.last[0].capture_groups.length : 0) + 1 : 0 ]
|
6
|
+
@replacements_regexp = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def replace( string )
|
10
|
+
string.gsub(replacement_regexp) do
|
11
|
+
m, r = *get_match_and_replacement( $~ )
|
12
|
+
execute_replacement_with_match r, m
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def get_match_and_replacement( m )
|
18
|
+
m = m.to_a.tap{ |m| m.shift }
|
19
|
+
i = m.find_index{ |v| v.present? }
|
20
|
+
r = find_replacement_by_index(i)
|
21
|
+
cps = [0] + (r[0].is_a?(Regexp) ? r[0].capture_groups : [])
|
22
|
+
m = m[i, cps.length]
|
23
|
+
[m, r[1]]
|
24
|
+
end
|
25
|
+
|
26
|
+
def execute_replacement_with_match( r, m )
|
27
|
+
r.is_a?(Proc) ? r.call(*m) : r.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_replacement_by_index( i )
|
31
|
+
@replacements.find{ |r| r[2] == i }
|
32
|
+
end
|
33
|
+
|
34
|
+
def replacement_regexp
|
35
|
+
@replacements_regexp ||= reconstruct_replacement_regexp
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def reconstruct_replacement_regexp
|
40
|
+
additional_offset = 1
|
41
|
+
pattern = @replacements.map(&:first).map{ |pat|
|
42
|
+
cpsc = pat.is_a?(Regexp) ? pat.capture_groups.length : 0
|
43
|
+
pat = pat.is_a?(Regexp) ? pat.source : Regexp.escape(pat.to_s)
|
44
|
+
pat.gsub!(/(?<!\\\\)(?<=\\)(\d+)/){ $1.to_i + additional_offset }
|
45
|
+
additional_offset += 1 + cpsc
|
46
|
+
"(#{pat})"
|
47
|
+
}.join "|"
|
48
|
+
/(?:#{pattern})/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe WillScanString::RegexpTraits do
|
4
|
+
it "should match regular capture groups" do
|
5
|
+
/(a)/.capture_groups.should eql [ 1 ]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not match escaped capture groups" do
|
9
|
+
/\(\)/.capture_groups.should eql []
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should match named capture groups with less-than & greater-than characters" do
|
13
|
+
/(?<a>a)/.capture_groups.should eql [ :a ]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should match named capture groups with single quotes" do
|
17
|
+
/(?'a'a)/.capture_groups.should eql [ :a ]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should match nested regular capture groups" do
|
21
|
+
/(a(b))/.capture_groups.should eql [ 1, 2 ]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should ignore non-capture groups" do
|
25
|
+
/(?:a)/.capture_groups.should eql []
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should ignore look-aheads and look-behinds" do
|
29
|
+
/(?=)/.capture_groups.should eql []
|
30
|
+
/(?!)/.capture_groups.should eql []
|
31
|
+
/(?<=)/.capture_groups.should eql []
|
32
|
+
/(?<!)/.capture_groups.should eql []
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should match the capture groups in my bbcode gem's regular expressions" do
|
36
|
+
/\[(\/?)([a-z0-9_-]*)(\s*=?(?:(?:\s*(?:(?:[a-z0-9_-]+)|(?<=\=))\s*[:=]\s*)?(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]*(?:\\[\s\S][^'\\]*)*'|[^\]\s,]+|(?<=,)(?=\s*,))\s*,?\s*)*)\]/i.capture_groups.should \
|
37
|
+
eql [ 1, 2, 3 ]
|
38
|
+
/(?:\s*(?:([a-z0-9_-]+)|^)\s*[:=]\s*)?("[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]*(?:\\[\s\S][^'\\]*)*'|[^\]\s,]+|(?<=,)(?=\s*,))\s*,?/i.capture_groups.should \
|
39
|
+
eql [ 1, 2 ]
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe WillScanString::StringScanner do
|
4
|
+
it "should perform a regular replacement" do
|
5
|
+
ss = WillScanString::StringScanner.new
|
6
|
+
ss.register_replacement ":)", "HAPPY"
|
7
|
+
ss.replace(":)").should eql "HAPPY"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should perform a regexp replacement with a capture group" do
|
11
|
+
ss = WillScanString::StringScanner.new
|
12
|
+
ss.register_replacement %r{<([a-z]+)>.*?</\1>}, ->(_, tagname){ "#{tagname}" }
|
13
|
+
ss.replace("<strong>hi!</strong>").should eql("strong")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not replace replaced strings" do
|
17
|
+
ss = WillScanString::StringScanner.new
|
18
|
+
ss.register_replacement ":)", %{<img src="happy.png" alt=":)" title=":)">}
|
19
|
+
ss.register_replacement "<", "<"
|
20
|
+
ss.register_replacement ">", ">"
|
21
|
+
ss.register_replacement "\"", """
|
22
|
+
ss.register_replacement "&", "&"
|
23
|
+
ss.replace("& :)").should eql %{& <img src="happy.png" alt=":)" title=":)">}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to use multiple regular expressions to replace with" do
|
27
|
+
ss = WillScanString::StringScanner.new
|
28
|
+
ss.register_replacement /(a)(b)/, "AB"
|
29
|
+
ss.register_replacement /(c)(d)/, "CD"
|
30
|
+
ss.replace("abcd").should eql "ABCD"
|
31
|
+
end
|
32
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "will_scan_string/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "will_scan_string"
|
7
|
+
s.version = WillScanString::VERSION
|
8
|
+
s.authors = ["Toby Hinloopen"]
|
9
|
+
s.email = ["toby@kutcomputers.nl"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Gem for string replacements using multiple regular expressions in a single pass.}
|
12
|
+
s.description = %q{Gem for string replacements using multiple regular expressions in a single pass.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "will_scan_string"
|
15
|
+
|
16
|
+
s.add_development_dependency "rspec", "~> 2.6"
|
17
|
+
s.add_dependency "activesupport", "~> 3.0.9"
|
18
|
+
s.add_dependency "i18n", "~> 0.5.0"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: will_scan_string
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Toby Hinloopen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-04 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70303473805000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.6'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70303473805000
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &70303473803740 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.9
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70303473803740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: i18n
|
38
|
+
requirement: &70303473802140 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.5.0
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70303473802140
|
47
|
+
description: Gem for string replacements using multiple regular expressions in a single
|
48
|
+
pass.
|
49
|
+
email:
|
50
|
+
- toby@kutcomputers.nl
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- Gemfile
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- lib/will_scan_string.rb
|
60
|
+
- lib/will_scan_string/regexp_traits.rb
|
61
|
+
- lib/will_scan_string/string_scanner.rb
|
62
|
+
- lib/will_scan_string/version.rb
|
63
|
+
- spec/lib/will_scan_string/regexp_traits_spec.rb
|
64
|
+
- spec/lib/will_scan_string/string_scanner_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
- will_scan_string.gemspec
|
67
|
+
homepage: ''
|
68
|
+
licenses: []
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project: will_scan_string
|
87
|
+
rubygems_version: 1.8.11
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Gem for string replacements using multiple regular expressions in a single
|
91
|
+
pass.
|
92
|
+
test_files:
|
93
|
+
- spec/lib/will_scan_string/regexp_traits_spec.rb
|
94
|
+
- spec/lib/will_scan_string/string_scanner_spec.rb
|
95
|
+
- spec/spec_helper.rb
|