redaranj-twitter-text 1.0.4.191

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.
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2010 Twitter, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
4
+ use this file except in compliance with the License. You may obtain a copy of
5
+ the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+ License for the specific language governing permissions and limitations under
13
+ the License.
@@ -0,0 +1,66 @@
1
+ == twitter-text
2
+
3
+ A gem that provides text processing routines for Twitter Tweets. The major
4
+ reason for this is to unify the various auto-linking and extraction of
5
+ usernames, lists, hashtags and URLs.
6
+
7
+ == Extraction Examples
8
+
9
+ # Extraction
10
+ class MyClass
11
+ include Twitter::Extractor
12
+ usernames = extract_mentioned_screen_names("Mentioning @twitter and @jack")
13
+ # usernames = ["twitter", "jack"]
14
+ end
15
+
16
+ # Extraction with a block argument
17
+ class MyClass
18
+ include Twitter::Extractor
19
+ extract_reply_screen_name("@twitter are you hiring?").do |username|
20
+ # username = "twitter"
21
+ end
22
+ end
23
+
24
+ == Auto-linking Examples
25
+
26
+ # Auto-link
27
+ class MyClass
28
+ include Twitter::Autolink
29
+
30
+ html = auto_link("link @user, please #request")
31
+ end
32
+
33
+ === Usernames
34
+
35
+ Username extraction and linking matches all valid Twitter usernames but does
36
+ not verify that the username is a valid Twitter account.
37
+
38
+ === Lists
39
+
40
+ Auto-link and extract list names when they are written in @user/list-name
41
+ format.
42
+
43
+ === Hashtags
44
+
45
+ Auto-link and extract hashtags, where a hashtag contains any latin letter or
46
+ number but cannot be solely numbers.
47
+
48
+ === URLs
49
+
50
+ Auto-linking and extraction of URLs differs from the Rails default so that it
51
+ will work correctly in Tweets written in languages that do not include spaces
52
+ between words.
53
+
54
+ === International
55
+
56
+ Special care has been taken to be sure that auto-linking and extraction work
57
+ in Tweets of all languages. This means that languages without spaces between
58
+ words should work equally well.
59
+
60
+ === Conformance
61
+
62
+ To run the Conformance suite, you'll need to add that project as a git submodule. From the root twitter-text-rb directory, run:
63
+
64
+ git submodule add git@github.com:mzsanford/twitter-text-conformance.git test/twitter-text-conformance/
65
+ git submodule init
66
+ git submodule update
@@ -0,0 +1,115 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ require 'rake/gempackagetask'
3
+ require 'rake/rdoctask'
4
+ require 'rubygems/specification'
5
+ require 'date'
6
+ require 'spec/rake/spectask'
7
+ require 'spec/rake/verify_rcov'
8
+ require 'digest'
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = "twitter-text"
12
+ s.version = "1.0.4"
13
+ s.authors = ["Matt Sanford", "Patrick Ewing"]
14
+ s.email = ["matt@twitter.com", "patrick.henry.ewing@gmail.com"]
15
+ s.homepage = "http://twitter.com"
16
+ s.description = s.summary = "A gem that provides text handling for Twitter"
17
+
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.summary = "Twitter text handling library"
21
+
22
+ s.add_dependency "actionpack"
23
+
24
+ s.require_path = 'lib'
25
+ s.autorequire = ''
26
+ s.files = %w(LICENSE README.rdoc Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
27
+ end
28
+
29
+ task :default => :spec
30
+
31
+ desc "Run specs"
32
+ Spec::Rake::SpecTask.new do |t|
33
+ t.spec_files = FileList['spec/**/*_spec.rb']
34
+ t.spec_opts = %w(-fs --color)
35
+ end
36
+
37
+ desc "Run all examples with RCov"
38
+ Spec::Rake::SpecTask.new('spec:rcov') do |t|
39
+ t.spec_files = FileList['spec/**/*.rb']
40
+ t.rcov = true
41
+ t.rcov_opts = ['--exclude', 'spec']
42
+ end
43
+
44
+
45
+ def conformance_version(dir)
46
+ Dir[File.join(dir, '*')].inject(Digest::SHA1.new){|digest, file| digest.update(Digest::SHA1.file(file).hexdigest) }
47
+ end
48
+
49
+ namespace :test do
50
+ namespace :conformance do
51
+
52
+
53
+ desc "Update conformance testing data"
54
+ task :update do
55
+ puts "Updating conformance data ... "
56
+ system("git submodule init") || raise("Failed to init submodule")
57
+ system("git submodule update") || raise("Failed to update submodule")
58
+ puts "Updating conformance data ... DONE"
59
+ end
60
+
61
+ desc "Change conformance test data to the lastest version"
62
+ task :latest => ['conformance:update'] do
63
+ current_dir = File.dirname(__FILE__)
64
+ submodule_dir = File.join(File.dirname(__FILE__), "test", "twitter-text-conformance")
65
+ version_before = conformance_version(submodule_dir)
66
+ system("cd #{submodule_dir} && git pull origin master") || raise("Failed to pull submodule version")
67
+ system("cd #{current_dir}")
68
+ if conformance_version(submodule_dir) != version_before
69
+ system("cd #{current_dir} && git add #{submodule_dir}") || raise("Failed to add upgrade files")
70
+ system("git commit -m \"Upgraded to the latest conformance suite\" #{submodule_dir}") || raise("Failed to commit upgraded conformacne data")
71
+ puts "Upgraded conformance suite."
72
+ else
73
+ puts "No conformance suite changes."
74
+ end
75
+ end
76
+
77
+ desc "Run conformance test suite"
78
+ task :run do
79
+ ruby "test/conformance_test.rb"
80
+ end
81
+ end
82
+
83
+ desc "Run conformance test suite"
84
+ task :conformance => ['conformance:latest', 'conformance:run'] do
85
+ end
86
+ end
87
+
88
+
89
+ namespace :doc do
90
+ Rake::RDocTask.new do |rd|
91
+ rd.main = "README.rdoc"
92
+ rd.rdoc_dir = 'doc'
93
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
94
+ end
95
+ end
96
+
97
+ Rake::GemPackageTask.new(spec) do |pkg|
98
+ pkg.gem_spec = spec
99
+ end
100
+
101
+ desc "install the gem locally"
102
+ task :install => [:package] do
103
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
104
+ end
105
+
106
+ desc "create a gemspec file"
107
+ task :make_spec do
108
+ File.open("#{GEM}.gemspec", "w") do |file|
109
+ file.puts spec.to_ruby
110
+ end
111
+ end
112
+
113
+ desc "runs cruise control build"
114
+ task :cruise => [:spec, 'test:conformance'] do
115
+ end
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ TODO:
2
+
3
+ * More tests
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+ module Twitter
3
+ # A module for including Tweet auto-linking in a class. The primary use of this is for helpers/views so they can auto-link
4
+ # usernames, lists, hashtags and URLs.
5
+ module Autolink
6
+ include ActionView::Helpers::TagHelper #tag_options needed by auto_link
7
+
8
+ WWW_REGEX = /www\./i #:nodoc:
9
+
10
+ # Default CSS class for auto-linked URLs
11
+ DEFAULT_URL_CLASS = "tweet-url"
12
+ # Default CSS class for auto-linked lists (along with the url class)
13
+ DEFAULT_LIST_CLASS = "list-slug"
14
+ # Default CSS class for auto-linked usernames (along with the url class)
15
+ DEFAULT_USERNAME_CLASS = "username"
16
+ # Default CSS class for auto-linked hashtags (along with the url class)
17
+ DEFAULT_HASHTAG_CLASS = "hashtag"
18
+ # HTML attribute for robot nofollow behavior (default)
19
+ HTML_ATTR_NO_FOLLOW = " rel=\"nofollow\""
20
+
21
+ # Add <tt><a></a></tt> tags around the usernames, lists, hashtags and URLs in the provided <tt>text</tt>. The
22
+ # <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt>
23
+ # hash:
24
+ #
25
+ # <tt>:url_class</tt>:: class to add to all <tt><a></tt> tags
26
+ # <tt>:list_class</tt>:: class to add to list <tt><a></tt> tags
27
+ # <tt>:username_class</tt>:: class to add to username <tt><a></tt> tags
28
+ # <tt>:hashtag_class</tt>:: class to add to hashtag <tt><a></tt> tags
29
+ # <tt>:username_url_base</tt>:: the value for <tt>href</tt> attribute on username links. The <tt>@username</tt> (minus the <tt>@</tt>) will be appended at the end of this.
30
+ # <tt>:list_url_base</tt>:: the value for <tt>href</tt> attribute on list links. The <tt>@username/list</tt> (minus the <tt>@</tt>) will be appended at the end of this.
31
+ # <tt>:hashtag_url_base</tt>:: the value for <tt>href</tt> attribute on hashtag links. The <tt>#hashtag</tt> (minus the <tt>#</tt>) will be appended at the end of this.
32
+ # <tt>:suppress_lists</tt>:: disable auto-linking to lists
33
+ # <tt>:suppress_no_follow</tt>:: Do not add <tt>rel="nofollow"</tt> to auto-linked items
34
+ def auto_link(text, options = {})
35
+ auto_link_usernames_or_lists(
36
+ auto_link_urls_custom(
37
+ auto_link_hashtags(text, options),
38
+ options),
39
+ options)
40
+ end
41
+
42
+ # Add <tt><a></a></tt> tags around the usernames and lists in the provided <tt>text</tt>. The
43
+ # <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt>
44
+ # hash:
45
+ #
46
+ # <tt>:url_class</tt>:: class to add to all <tt><a></tt> tags
47
+ # <tt>:list_class</tt>:: class to add to list <tt><a></tt> tags
48
+ # <tt>:username_class</tt>:: class to add to username <tt><a></tt> tags
49
+ # <tt>:username_url_base</tt>:: the value for <tt>href</tt> attribute on username links. The <tt>@username</tt> (minus the <tt>@</tt>) will be appended at the end of this.
50
+ # <tt>:list_url_base</tt>:: the value for <tt>href</tt> attribute on list links. The <tt>@username/list</tt> (minus the <tt>@</tt>) will be appended at the end of this.
51
+ # <tt>:suppress_lists</tt>:: disable auto-linking to lists
52
+ # <tt>:suppress_no_follow</tt>:: Do not add <tt>rel="nofollow"</tt> to auto-linked items
53
+ def auto_link_usernames_or_lists(text, options = {}) # :yields: list_or_username
54
+ options = options.dup
55
+ options[:url_class] ||= DEFAULT_URL_CLASS
56
+ options[:list_class] ||= DEFAULT_LIST_CLASS
57
+ options[:username_class] ||= DEFAULT_USERNAME_CLASS
58
+ options[:username_url_base] ||= "http://twitter.com/"
59
+ options[:list_url_base] ||= "http://twitter.com/"
60
+ extra_html = HTML_ATTR_NO_FOLLOW unless options[:suppress_no_follow]
61
+
62
+ text.gsub(Twitter::Regex[:auto_link_usernames_or_lists]) do
63
+ if $4 && !options[:suppress_lists]
64
+ # the link is a list
65
+ text = list = "#{$3}#{$4}"
66
+ text = yield(list) if block_given?
67
+ "#{$1}#{$2}<a class=\"#{options[:url_class]} #{options[:list_class]}\" href=\"#{options[:list_url_base]}#{list.downcase}\"#{extra_html}>#{text}</a>"
68
+ else
69
+ # this is a screen name
70
+ text = $3
71
+ text = yield(text) if block_given?
72
+ "#{$1}#{$2}<a class=\"#{options[:url_class]} #{options[:username_class]}\" href=\"#{options[:username_url_base]}#{text}\"#{extra_html}>#{text}</a>"
73
+ end
74
+ end
75
+ end
76
+
77
+ # Add <tt><a></a></tt> tags around the hashtags in the provided <tt>text</tt>. The
78
+ # <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt>
79
+ # hash:
80
+ #
81
+ # <tt>:url_class</tt>:: class to add to all <tt><a></tt> tags
82
+ # <tt>:hashtag_class</tt>:: class to add to hashtag <tt><a></tt> tags
83
+ # <tt>:hashtag_url_base</tt>:: the value for <tt>href</tt> attribute. The hashtag text (minus the <tt>#</tt>) will be appended at the end of this.
84
+ # <tt>:suppress_no_follow</tt>:: Do not add <tt>rel="nofollow"</tt> to auto-linked items
85
+ def auto_link_hashtags(text, options = {}) # :yields: hashtag_text
86
+ options = options.dup
87
+ options[:url_class] ||= DEFAULT_URL_CLASS
88
+ options[:hashtag_class] ||= DEFAULT_HASHTAG_CLASS
89
+ options[:hashtag_url_base] ||= "http://twitter.com/search?q=%23"
90
+ extra_html = HTML_ATTR_NO_FOLLOW unless options[:suppress_no_follow]
91
+
92
+ text.gsub(Twitter::Regex[:auto_link_hashtags]) do
93
+ before = $1
94
+ hash = $2
95
+ text = $3
96
+ text = yield(text) if block_given?
97
+ "#{before}<a href=\"#{options[:hashtag_url_base]}#{text}\" title=\"##{text}\" class=\"#{options[:url_class]} #{options[:hashtag_class]}\"#{extra_html}>#{hash}#{text}</a>"
98
+ end
99
+ end
100
+
101
+ # Add <tt><a></a></tt> tags around the URLs in the provided <tt>text</tt>. Any
102
+ # elements in the <tt>href_options</tt> hash will be converted to HTML attributes
103
+ # and place in the <tt><a></tt> tag. Unless <tt>href_options</tt> contains <tt>:suppress_no_follow</tt>
104
+ # the <tt>rel="nofollow"</tt> attribute will be added.
105
+ def auto_link_urls_custom(text, href_options = {})
106
+ options = href_options.dup
107
+ options[:rel] = "nofollow" unless options.delete(:suppress_no_follow)
108
+
109
+ text.gsub(Twitter::Regex[:valid_url]) do
110
+ all, before, url, protocol = $1, $2, $3, $4
111
+ html_attrs = tag_options(options.stringify_keys) || ""
112
+ full_url = (protocol =~ WWW_REGEX ? "http://#{url}" : url)
113
+ "#{before}<a href=\"#{full_url}\"#{html_attrs}>#{url}</a>"
114
+ end
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+ module Twitter
3
+ # A module for including Tweet parsing in a class. This module provides function for the extraction and processing
4
+ # of usernames, lists, URLs and hashtags.
5
+ module Extractor
6
+
7
+ # Extracts a list of all usernames mentioned in the Tweet <tt>text</tt>. If the
8
+ # <tt>text</tt> is <tt>nil</tt> or contains no username mentions an empty array
9
+ # will be returned.
10
+ #
11
+ # If a block is given then it will be called for each username.
12
+ def extract_mentioned_screen_names(text) # :yields: username
13
+ return [] unless text
14
+
15
+ possible_screen_names = []
16
+ text.scan(Twitter::Regex[:extract_mentions]) do |before, sn, after|
17
+ possible_screen_names << sn unless after =~ Twitter::Regex[:at_signs]
18
+ end
19
+ possible_screen_names.each{|sn| yield sn } if block_given?
20
+ possible_screen_names
21
+ end
22
+
23
+ # Extracts the username username replied to in the Tweet <tt>text</tt>. If the
24
+ # <tt>text</tt> is <tt>nil</tt> or is not a reply nil will be returned.
25
+ #
26
+ # If a block is given then it will be called with the username replied to (if any)
27
+ def extract_reply_screen_name(text) # :yields: username
28
+ return nil unless text
29
+
30
+ possible_screen_name = text.match(Twitter::Regex[:extract_reply])
31
+ return unless possible_screen_name.respond_to?(:captures)
32
+ screen_name = possible_screen_name.captures.first
33
+ yield screen_name if block_given?
34
+ screen_name
35
+ end
36
+
37
+ # Extracts a list of all URLs included in the Tweet <tt>text</tt>. If the
38
+ # <tt>text</tt> is <tt>nil</tt> or contains no URLs an empty array
39
+ # will be returned.
40
+ #
41
+ # If a block is given then it will be called for each URL.
42
+ def extract_urls(text) # :yields: url
43
+ return [] unless text
44
+ urls = []
45
+ text.to_s.scan(Twitter::Regex[:valid_url]) do |all, before, url, protocol, domain, path, query|
46
+ urls << (protocol == "www." ? "http://#{url}" : url)
47
+ end
48
+ urls.each{|url| yield url } if block_given?
49
+ urls
50
+ end
51
+
52
+ # Extracts a list of all hashtags included in the Tweet <tt>text</tt>. If the
53
+ # <tt>text</tt> is <tt>nil</tt> or contains no hashtags an empty array
54
+ # will be returned. The array returned will not include the leading <tt>#</tt>
55
+ # character.
56
+ #
57
+ # If a block is given then it will be called for each hashtag.
58
+ def extract_hashtags(text) # :yields: hashtag_text
59
+ return [] unless text
60
+
61
+ tags = []
62
+ text.scan(Twitter::Regex[:auto_link_hashtags]) do |before, hash, hash_text|
63
+ tags << hash_text
64
+ end
65
+ tags.each{|tag| yield tag } if block_given?
66
+ tags
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+ module Twitter
3
+ # A collection of regular expressions for parsing Tweet text. The regular expression
4
+ # list is frozen at load time to ensure immutability. These reular expressions are
5
+ # used throughout the <tt>Twitter</tt> classes. Special care has been taken to make
6
+ # sure these reular expressions work with Tweets in all languages.
7
+ class Regex
8
+ REGEXEN = {} # :nodoc:
9
+
10
+ # Space is more than %20, U+3000 for example is the full-width space used with Kanji. Provide a short-hand
11
+ # to access both the list of characters and a pattern suitible for use with String#split
12
+ # Taken from: ActiveSupport::Multibyte::Handlers::UTF8Handler::UNICODE_WHITESPACE
13
+ UNICODE_SPACES = [
14
+ (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
15
+ 0x0020, # White_Space # Zs SPACE
16
+ 0x0085, # White_Space # Cc <control-0085>
17
+ 0x00A0, # White_Space # Zs NO-BREAK SPACE
18
+ 0x1680, # White_Space # Zs OGHAM SPACE MARK
19
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
20
+ (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
21
+ 0x2028, # White_Space # Zl LINE SEPARATOR
22
+ 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
23
+ 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
24
+ 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
25
+ 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
26
+ ].flatten.freeze
27
+ REGEXEN[:spaces] = Regexp.new(UNICODE_SPACES.collect{ |e| [e].pack 'U*' }.join('|'))
28
+
29
+ REGEXEN[:at_signs] = /[@@]/
30
+ REGEXEN[:extract_mentions] = /(^|[^a-zA-Z0-9_])#{REGEXEN[:at_signs]}([a-zA-Z0-9_]{1,20})(?=(.|$))/o
31
+ REGEXEN[:extract_reply] = /^(?:#{REGEXEN[:spaces]})*#{REGEXEN[:at_signs]}([a-zA-Z0-9_]{1,20})/o
32
+
33
+ REGEXEN[:list_name] = /^[a-zA-Z].{0,79}$/
34
+
35
+ # Latin accented characters (subtracted 0xD7 from the range, it's a confusable multiplication sign. Looks like "x")
36
+ LATIN_ACCENTS = [(0xc0..0xd6).to_a, (0xd8..0xf6).to_a, (0xf8..0xff).to_a].flatten.pack('U*').freeze
37
+ REGEXEN[:latin_accents] = /[#{LATIN_ACCENTS}]+/o
38
+
39
+ # Characters considered valid in a hashtag but not at the beginning, where only a-z and 0-9 are valid.
40
+ HASHTAG_CHARACTERS = /[a-z0-9_#{LATIN_ACCENTS}]/io
41
+ REGEXEN[:auto_link_hashtags] = /(^|[^0-9A-Z&\/]+)(#|#)([0-9A-Z_]*[A-Z_]+#{HASHTAG_CHARACTERS}*)/io
42
+ REGEXEN[:auto_link_usernames_or_lists] = /([^a-zA-Z0-9_]|^)([@@]+)([a-zA-Z0-9_]{1,20})(\/[a-zA-Z][a-zA-Z0-9\-]{0,79})?/
43
+ REGEXEN[:auto_link_emoticon] = /(8\-\#|8\-E|\+\-\(|\`\@|\`O|\&lt;\|:~\(|\}:o\{|:\-\[|\&gt;o\&lt;|X\-\/|\[:-\]\-I\-|\/\/\/\/Ö\\\\\\\\|\(\|:\|\/\)|∑:\*\)|\( \| \))/
44
+
45
+ # URL related hash regex collection
46
+ REGEXEN[:valid_preceding_chars] = /(?:[^\/"':!=]|^|\:)/
47
+ REGEXEN[:valid_domain] = /(?:[^[:punct:]\s][\.-][^[:punct:]\s]|[^[:punct:]\s])+\.[a-z]{2,}(?::[0-9]+)?/i
48
+ REGEXEN[:valid_url_path_chars] = /[\.\,]?[a-z0-9!\*'\(\);:=\+\$\/%#\[\]\-_,~@]/i
49
+ # Valid end-of-path chracters (so /foo. does not gobble the period).
50
+ # 1. Allow ) for Wikipedia URLs.
51
+ # 2. Allow =&# for empty URL parameters and other URL-join artifacts
52
+ REGEXEN[:valid_url_path_ending_chars] = /[a-z0-9\)=#\/]/i
53
+ REGEXEN[:valid_url_query_chars] = /[a-z0-9!\*'\(\);:&=\+\$\/%#\[\]\-_\.,~]/i
54
+ REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#]/i
55
+ REGEXEN[:valid_url] = %r{
56
+ ( # $1 total match
57
+ (#{REGEXEN[:valid_preceding_chars]}) # $2 Preceeding chracter
58
+ ( # $3 URL
59
+ (https?:\/\/|www\.) # $4 Protocol or beginning
60
+ (#{REGEXEN[:valid_domain]}) # $5 Domain(s) and optional post number
61
+ (/#{REGEXEN[:valid_url_path_chars]}*#{REGEXEN[:valid_url_path_ending_chars]}?)? # $6 URL Path
62
+ (\?#{REGEXEN[:valid_url_query_chars]}*#{REGEXEN[:valid_url_query_ending_chars]})? # $7 Query String
63
+ )
64
+ )
65
+ }iox;
66
+
67
+ REGEXEN.each_pair{|k,v| v.freeze }
68
+
69
+ # Return the regular expression for a given <tt>key</tt>. If the <tt>key</tt>
70
+ # is not a known symbol a <tt>nil</tt> will be returned.
71
+ def self.[](key)
72
+ REGEXEN[key]
73
+ end
74
+ end
75
+ end