twitter-text 1.2.4 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ pkg/*
5
+ coverage
6
+ doc
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "test/twitter-text-conformance"]
2
+ path = test/twitter-text-conformance
3
+ url = git://github.com/mzsanford/twitter-text-conformance.git
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=nested
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify the gem's dependencies in twitter-text.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ twitter-text (1.2.5)
5
+ actionpack
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ abstract (1.0.0)
11
+ actionpack (3.0.1)
12
+ activemodel (= 3.0.1)
13
+ activesupport (= 3.0.1)
14
+ builder (~> 2.1.2)
15
+ erubis (~> 2.6.6)
16
+ i18n (~> 0.4.1)
17
+ rack (~> 1.2.1)
18
+ rack-mount (~> 0.6.12)
19
+ rack-test (~> 0.5.4)
20
+ tzinfo (~> 0.3.23)
21
+ activemodel (3.0.1)
22
+ activesupport (= 3.0.1)
23
+ builder (~> 2.1.2)
24
+ i18n (~> 0.4.1)
25
+ activesupport (3.0.1)
26
+ builder (2.1.2)
27
+ diff-lcs (1.1.2)
28
+ erubis (2.6.6)
29
+ abstract (>= 1.0.0)
30
+ i18n (0.4.2)
31
+ nokogiri (1.4.3.1)
32
+ nokogiri (1.4.3.1-java)
33
+ weakling (>= 0.0.3)
34
+ rack (1.2.1)
35
+ rack-mount (0.6.13)
36
+ rack (>= 1.0.0)
37
+ rack-test (0.5.6)
38
+ rack (>= 1.0)
39
+ rake (0.8.7)
40
+ rspec (2.1.0)
41
+ rspec-core (~> 2.1.0)
42
+ rspec-expectations (~> 2.1.0)
43
+ rspec-mocks (~> 2.1.0)
44
+ rspec-core (2.1.0)
45
+ rspec-expectations (2.1.0)
46
+ diff-lcs (~> 1.1.2)
47
+ rspec-mocks (2.1.0)
48
+ simplecov (0.3.7)
49
+ simplecov-html (>= 0.3.7)
50
+ simplecov-html (0.3.9)
51
+ tzinfo (0.3.23)
52
+ weakling (0.0.4-java)
53
+
54
+ PLATFORMS
55
+ java
56
+ ruby
57
+
58
+ DEPENDENCIES
59
+ actionpack
60
+ nokogiri
61
+ rake
62
+ rspec
63
+ simplecov
64
+ twitter-text!
data/Rakefile CHANGED
@@ -1,50 +1,13 @@
1
- require 'rubygems' unless ENV['NO_RUBYGEMS']
2
- require 'rake/gempackagetask'
3
- require 'rake/rdoctask'
4
- require 'rubygems/specification'
5
- require 'date'
6
-
7
- gem 'rspec'
8
- require 'spec/rake/spectask'
9
- require 'spec/rake/verify_rcov'
10
- require 'digest'
11
-
12
- spec = Gem::Specification.new do |s|
13
- s.name = "twitter-text"
14
- s.version = "1.2.4"
15
- s.authors = ["Matt Sanford", "Patrick Ewing", "Ben Cherry", "Britt Selvitelle", "Raffi Krikorian"]
16
- s.email = ["matt@twitter.com", "patrick.henry.ewing@gmail.com", "bcherry@gmail.com", "bs@brittspace.com", "raffi@twitter.com"]
17
- s.homepage = "http://twitter.com"
18
- s.description = s.summary = "A gem that provides text handling for Twitter"
19
-
20
- s.platform = Gem::Platform::RUBY
21
- s.has_rdoc = true
22
- s.summary = "Twitter text handling library"
23
-
24
- s.add_dependency "actionpack"
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
25
3
 
26
- s.require_path = 'lib'
27
- s.autorequire = ''
28
- s.files = %w(LICENSE README.rdoc Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
29
- end
30
-
31
- task :default => :spec
32
-
33
- desc "Run specs"
34
- Spec::Rake::SpecTask.new do |t|
35
- t.spec_files = FileList['spec/**/*_spec.rb']
36
- t.spec_opts = %w(-fs --color)
37
- t.libs << ["spec", '.']
38
- end
4
+ task :default => ["spec", "test:conformance"]
39
5
 
40
- desc "Run all examples with RCov"
41
- Spec::Rake::SpecTask.new('spec:rcov') do |t|
42
- t.spec_files = FileList['spec/**/*.rb']
43
- t.rcov = true
44
- t.rcov_opts = ['--exclude', 'spec']
45
- end
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec)
46
8
 
47
9
  def conformance_version(dir)
10
+ require 'digest'
48
11
  Dir[File.join(dir, '*')].inject(Digest::SHA1.new){|digest, file| digest.update(Digest::SHA1.file(file).hexdigest) }
49
12
  end
50
13
 
@@ -85,6 +48,7 @@ namespace :test do
85
48
  end
86
49
  end
87
50
 
51
+ require 'rake/rdoctask'
88
52
  namespace :doc do
89
53
  Rake::RDocTask.new do |rd|
90
54
  rd.main = "README.rdoc"
@@ -93,22 +57,6 @@ namespace :doc do
93
57
  end
94
58
  end
95
59
 
96
- Rake::GemPackageTask.new(spec) do |pkg|
97
- pkg.gem_spec = spec
98
- end
99
-
100
- desc "install the gem locally"
101
- task :install => [:package] do
102
- sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
103
- end
104
-
105
- desc "create a gemspec file"
106
- task :make_spec do
107
- File.open("#{GEM}.gemspec", "w") do |file|
108
- file.puts spec.to_ruby
109
- end
110
- end
111
-
112
- desc "runs cruise control build"
60
+ desc "Run cruise control build"
113
61
  task :cruise => [:spec, 'test:conformance'] do
114
62
  end
data/lib/autolink.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  module Twitter
3
2
  # 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
3
  # usernames, lists, hashtags and URLs.
@@ -140,10 +139,17 @@ module Twitter
140
139
 
141
140
  text.gsub(Twitter::Regex[:valid_url]) do
142
141
  all, before, url, protocol, domain, path, query_string = $1, $2, $3, $4, $5, $6, $7
143
- if !protocol.blank? || domain =~ Twitter::Regex[:probable_tld]
142
+ if !protocol.blank? # || domain =~ Twitter::Regex[:probable_tld_domain]
144
143
  html_attrs = tag_options(options.stringify_keys) || ""
145
144
  full_url = ((protocol =~ Twitter::Regex[:www] || protocol.blank?) ? "http://#{url}" : url)
146
145
  "#{before}<a href=\"#{html_escape(full_url)}\"#{html_attrs}>#{html_escape(url)}</a>"
146
+ elsif all =~ Twitter::Regex[:probable_tld_domain]
147
+ before_tld, tld_domain = $1, $2
148
+
149
+ html_attrs = tag_options(options.stringify_keys) || ""
150
+ full_url = "http://#{tld_domain}"
151
+ prefix = (before_tld == before ? before : "#{before}#{before_tld}")
152
+ "#{prefix}<a href=\"#{html_escape(full_url)}\"#{html_attrs}>#{html_escape(tld_domain)}</a>"
147
153
  else
148
154
  all
149
155
  end
@@ -151,4 +157,4 @@ module Twitter
151
157
  end
152
158
 
153
159
  end
154
- end
160
+ end
data/lib/extractor.rb CHANGED
@@ -20,18 +20,23 @@ class String
20
20
  char_array
21
21
  end
22
22
  end
23
+ end
23
24
 
24
- # Helper function to find the index of the <tt>sub_string</tt> in
25
- # <tt>str</tt>. This is needed because with unicode strings, the return
26
- # of index may be incorrect.
27
- def sub_string_search(sub_str, position = 0)
28
- if respond_to? :codepoints
29
- index(sub_str, position)
25
+ # Helper functions to return character offsets instead of byte offsets.
26
+ class MatchData
27
+ def char_begin(n)
28
+ if string.respond_to? :codepoints
29
+ self.begin(n)
30
30
  else
31
- index = to_char_a[position..-1].each_with_index.find do |e|
32
- to_char_a.slice(e.last + position, sub_str.char_length).map{|ci| ci.first }.join == sub_str
33
- end
34
- index.nil? ? -1 : index.last + position
31
+ string[0, self.begin(n)].char_length
32
+ end
33
+ end
34
+
35
+ def char_end(n)
36
+ if string.respond_to? :codepoints
37
+ self.end(n)
38
+ else
39
+ string[0, self.end(n)].char_length
35
40
  end
36
41
  end
37
42
  end
@@ -63,14 +68,14 @@ module Twitter
63
68
  return [] unless text
64
69
 
65
70
  possible_screen_names = []
66
- position = 0
67
71
  text.to_s.scan(Twitter::Regex[:extract_mentions]) do |before, sn, after|
72
+ extract_mentions_match_data = $~
68
73
  unless after =~ Twitter::Regex[:end_screen_name_match]
69
- start_position = text.to_s.sub_string_search(sn, position) - 1
70
- position = start_position + sn.char_length + 1
74
+ start_position = extract_mentions_match_data.char_begin(2) - 1
75
+ end_position = extract_mentions_match_data.char_end(2)
71
76
  possible_screen_names << {
72
77
  :screen_name => sn,
73
- :indices => [start_position, position]
78
+ :indices => [start_position, end_position]
74
79
  }
75
80
  end
76
81
  end
@@ -117,14 +122,22 @@ module Twitter
117
122
  urls = []
118
123
  position = 0
119
124
  text.to_s.scan(Twitter::Regex[:valid_url]) do |all, before, url, protocol, domain, path, query|
120
- if !protocol.blank? || domain =~ Twitter::Regex[:probable_tld]
121
- start_position = text.to_s.sub_string_search(url, position)
122
- end_position = start_position + url.char_length
123
- position = end_position
125
+ valid_url_match_data = $~
126
+ if !protocol.blank?
127
+ start_position = valid_url_match_data.char_begin(3)
128
+ end_position = valid_url_match_data.char_end(3)
124
129
  urls << {
125
130
  :url => ((protocol =~ Twitter::Regex[:www] || protocol.blank?) ? "http://#{url}" : url),
126
131
  :indices => [start_position, end_position]
127
132
  }
133
+ elsif all =~ Twitter::Regex[:probable_tld_domain]
134
+ tld_domain = $2
135
+ start_position = valid_url_match_data.char_begin(1) + $~.char_begin(2)
136
+ end_position = valid_url_match_data.char_begin(1) + $~.char_end(2)
137
+ urls << {
138
+ :url => "http://#{tld_domain}",
139
+ :indices => [start_position, end_position]
140
+ }
128
141
  end
129
142
  end
130
143
  urls.each{|url| yield url[:url], url[:indices].first, url[:indices].last } if block_given?
@@ -153,13 +166,12 @@ module Twitter
153
166
  return [] unless text
154
167
 
155
168
  tags = []
156
- position = 0
157
169
  text.scan(Twitter::Regex[:auto_link_hashtags]) do |before, hash, hash_text|
158
- start_position = text.to_s.sub_string_search(hash + hash_text, position)
159
- position = start_position + hash_text.char_length + 1
170
+ start_position = $~.char_begin(2)
171
+ end_position = $~.char_end(3)
160
172
  tags << {
161
173
  :hashtag => hash_text,
162
- :indices => [start_position, position]
174
+ :indices => [start_position, end_position]
163
175
  }
164
176
  end
165
177
  tags.each{|tag| yield tag[:hashtag], tag[:indices].first, tag[:indices].last } if block_given?
@@ -1,4 +1,3 @@
1
-
2
1
  module Twitter
3
2
  # Module for doing "hit highlighting" on tweets that have been auto-linked already.
4
3
  # Useful with the results returned from the Search API.
data/lib/regex.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
-
3
2
  module Twitter
4
3
  # A collection of regular expressions for parsing Tweet text. The regular expression
5
4
  # list is frozen at load time to ensure immutability. These reular expressions are
@@ -31,15 +30,17 @@ module Twitter
31
30
  REGEXEN[:extract_mentions] = /(^|[^a-zA-Z0-9_])#{REGEXEN[:at_signs]}([a-zA-Z0-9_]{1,20})(?=(.|$))/o
32
31
  REGEXEN[:extract_reply] = /^(?:#{REGEXEN[:spaces]})*#{REGEXEN[:at_signs]}([a-zA-Z0-9_]{1,20})/o
33
32
 
34
- major, minor, patch = RUBY_VERSION.split(/\./)
35
- if major.to_i >= 1 && minor.to_i >= 9
33
+ major, minor, patch = RUBY_VERSION.split('.')
34
+ if major.to_i >= 2 || major.to_i == 1 && minor.to_i >= 9 || (defined?(RUBY_ENGINE) && ["jruby", "rbx"].include?(RUBY_ENGINE))
36
35
  REGEXEN[:list_name] = /[a-zA-Z][a-zA-Z0-9_\-\u0080-\u00ff]{0,24}/
37
36
  else
38
- # This line barfs at compile time in Ruby 1.9.
37
+ # This line barfs at compile time in Ruby 1.9, JRuby, or Rubinius.
39
38
  REGEXEN[:list_name] = eval("/[a-zA-Z][a-zA-Z0-9_\\-\x80-\xff]{0,24}/")
40
39
  end
41
40
 
42
- # Latin accented characters (subtracted 0xD7 from the range, it's a confusable multiplication sign. Looks like "x")
41
+ # Latin accented characters
42
+ # Excludes 0xd7 from the range (the multiplication sign, confusable with "x").
43
+ # Also excludes 0xf7, the division sign
43
44
  LATIN_ACCENTS = [(0xc0..0xd6).to_a, (0xd8..0xf6).to_a, (0xf8..0xff).to_a].flatten.pack('U*').freeze
44
45
  REGEXEN[:latin_accents] = /[#{LATIN_ACCENTS}]+/o
45
46
 
@@ -47,7 +48,7 @@ module Twitter
47
48
 
48
49
  # Characters considered valid in a hashtag but not at the beginning, where only a-z and 0-9 are valid.
49
50
  HASHTAG_CHARACTERS = /[a-z0-9_#{LATIN_ACCENTS}]/io
50
- REGEXEN[:auto_link_hashtags] = /(^|[^0-9A-Z&\/\?]+)(#|#)([0-9A-Z_]*[A-Z_]+#{HASHTAG_CHARACTERS}*)/io
51
+ REGEXEN[:auto_link_hashtags] = /(^|[^0-9A-Z&\/\?]+)(#|#)([0-9a-z_]*[a-z_]+#{HASHTAG_CHARACTERS}*)/io
51
52
  REGEXEN[:auto_link_usernames_or_lists] = /([^a-zA-Z0-9_]|^|RT:?)([@@]+)([a-zA-Z0-9_]{1,20})(\/[a-zA-Z][a-zA-Z0-9_\-]{0,24})?/o
52
53
  REGEXEN[:auto_link_emoticon] = /(8\-\#|8\-E|\+\-\(|\`\@|\`O|\&lt;\|:~\(|\}:o\{|:\-\[|\&gt;o\&lt;|X\-\/|\[:-\]\-I\-|\/\/\/\/Ö\\\\\\\\|\(\|:\|\/\)|∑:\*\)|\( \| \))/
53
54
 
@@ -56,7 +57,7 @@ module Twitter
56
57
  REGEXEN[:valid_domain] = /(?:[^[:punct:]\s][\.-](?=[^[:punct:]\s])|[^[:punct:]\s]){1,}\.[a-z]{2,}(?::[0-9]+)?/i
57
58
 
58
59
  # For protocol-less URLs, we'll accept them if they end in one of a handful of likely TLDs
59
- REGEXEN[:probable_tld] = /\.(?:com|net|org|gov|edu)$/i
60
+ REGEXEN[:probable_tld_domain] = /^(.*?)((?:[a-z0-9_\.\-]+)\.(?:com|net|org|gov|edu))$/i
60
61
 
61
62
  REGEXEN[:www] = /www\./i
62
63
 
data/lib/twitter-text.rb CHANGED
@@ -1,15 +1,12 @@
1
-
2
1
  major, minor, patch = RUBY_VERSION.split('.')
3
2
 
4
- if major == 1 && minor < 9
3
+ if major.to_i == 1 && minor.to_i < 9
5
4
  # Ruby 1.8 KCODE check. Not needed on 1.9 and later.
6
- raise("twitter-text requires the $KCODE variable be set to 'UTF8' or 'u'") unless ['u','UTF8'].include?($KCODE)
5
+ raise("twitter-text requires the $KCODE variable be set to 'UTF8' or 'u'") unless $KCODE[0].chr =~ /u/i
7
6
  end
8
7
 
9
- require 'rubygems'
10
-
11
- # Needed for auto-linking
12
- gem 'actionpack'
8
+ # External libraries required. (for gems, use: ruby -rubygems ...)
9
+ require 'action_pack'
13
10
  require 'action_view'
14
11
 
15
12
  require File.join(File.dirname(__FILE__), 'regex')
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -36,7 +36,7 @@ describe Twitter::Autolink do
36
36
  def original_text; "meet@the beach"; end
37
37
 
38
38
  it "should not be linked" do
39
- Hpricot(@autolinked_text).search('a').should be_blank
39
+ Nokogiri::HTML(@autolinked_text).search('a').should be_blank
40
40
  end
41
41
  end
42
42
 
@@ -132,7 +132,7 @@ describe Twitter::Autolink do
132
132
  def original_text; "hello @/my-list"; end
133
133
 
134
134
  it "should NOT be linked" do
135
- Hpricot(@autolinked_text).search('a').should be_blank
135
+ Nokogiri::HTML(@autolinked_text).search('a').should be_blank
136
136
  end
137
137
  end
138
138
 
@@ -148,7 +148,7 @@ describe Twitter::Autolink do
148
148
  def original_text; "meet@the/beach"; end
149
149
 
150
150
  it "should not be linked" do
151
- Hpricot(@autolinked_text).search('a').should be_blank
151
+ Nokogiri::HTML(@autolinked_text).search('a').should be_blank
152
152
  end
153
153
  end
154
154
 
@@ -304,9 +304,9 @@ describe Twitter::Autolink do
304
304
  def original_text; "#{[0xFF03].pack('U')}twj_dev"; end
305
305
 
306
306
  it "should be linked" do
307
- link = Hpricot(@autolinked_text).at('a')
307
+ link = Nokogiri::HTML(@autolinked_text).search('a')
308
308
  (link.inner_text.respond_to?(:force_encoding) ? link.inner_text.force_encoding("utf-8") : link.inner_text).should == "#{[0xFF03].pack('U')}twj_dev"
309
- link['href'].should == 'http://twitter.com/search?q=%23twj_dev'
309
+ link.first['href'].should == 'http://twitter.com/search?q=%23twj_dev'
310
310
  end
311
311
  end
312
312
 
@@ -468,9 +468,9 @@ describe Twitter::Autolink do
468
468
  def original_text; "I like www.foobar.com dudes"; end
469
469
 
470
470
  it "links to the original text with the full href" do
471
- link = Hpricot(@autolinked_text).at('a')
471
+ link = Nokogiri::HTML(@autolinked_text).search('a')
472
472
  link.inner_text.should == 'www.foobar.com'
473
- link['href'].should == 'http://www.foobar.com'
473
+ link.first['href'].should == 'http://www.foobar.com'
474
474
  end
475
475
  end
476
476
 
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,27 @@
1
1
  $TESTING=true
2
+ $KCODE='u'
2
3
  $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
4
 
4
- require 'twitter-text'
5
- require 'hpricot'
6
- require 'spec/test_urls'
5
+ require 'nokogiri'
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_group 'Libraries', 'lib'
9
+ end
10
+
11
+ require File.expand_path('../../lib/twitter-text', __FILE__)
12
+ require File.expand_path('../test_urls', __FILE__)
7
13
 
8
- Spec::Runner.configure do |config|
14
+ RSpec.configure do |config|
9
15
  config.include TestUrls
10
16
  end
11
17
 
12
- Spec::Matchers.define :match_autolink_expression do
18
+ RSpec::Matchers.define :match_autolink_expression do
13
19
  match do |string|
14
20
  Twitter::Regex[:valid_url].match(string)
15
21
  end
16
22
  end
17
23
 
18
- Spec::Matchers.define :match_autolink_expression_in do |text|
24
+ RSpec::Matchers.define :match_autolink_expression_in do |text|
19
25
  match do |url|
20
26
  @match_data = Twitter::Regex[:valid_url].match(text)
21
27
  @match_data && @match_data.to_s.strip == url
@@ -26,9 +32,9 @@ Spec::Matchers.define :match_autolink_expression_in do |text|
26
32
  end
27
33
  end
28
34
 
29
- Spec::Matchers.define :have_autolinked_url do |url|
35
+ RSpec::Matchers.define :have_autolinked_url do |url|
30
36
  match do |text|
31
- @link = Hpricot(text).at("a[@href='#{url}']")
37
+ @link = Nokogiri::HTML(text).search("a[@href='#{url}']")
32
38
  @link &&
33
39
  @link.inner_text &&
34
40
  @link.inner_text == url
@@ -39,10 +45,10 @@ Spec::Matchers.define :have_autolinked_url do |url|
39
45
  end
40
46
  end
41
47
 
42
- Spec::Matchers.define :link_to_screen_name do |screen_name|
48
+ RSpec::Matchers.define :link_to_screen_name do |screen_name|
43
49
  match do |text|
44
- @link = Hpricot(text).at("a.username")
45
- @link && @link.inner_text == screen_name && "http://twitter.com/#{screen_name}".downcase.should == @link['href']
50
+ @link = Nokogiri::HTML(text).search("a.username")
51
+ @link && @link.inner_text == screen_name && "http://twitter.com/#{screen_name}".downcase.should == @link.first['href']
46
52
  end
47
53
 
48
54
  failure_message_for_should do |text|
@@ -58,10 +64,10 @@ Spec::Matchers.define :link_to_screen_name do |screen_name|
58
64
  end
59
65
  end
60
66
 
61
- Spec::Matchers.define :link_to_list_path do |list_path|
67
+ RSpec::Matchers.define :link_to_list_path do |list_path|
62
68
  match do |text|
63
- @link = Hpricot(text).at("a.list-slug")
64
- !@link.nil? && @link.inner_text == list_path && "http://twitter.com/#{list_path}".downcase.should == @link['href']
69
+ @link = Nokogiri::HTML(text).search("a.list-slug")
70
+ !@link.nil? && @link.inner_text == list_path && "http://twitter.com/#{list_path}".downcase.should == @link.first['href']
65
71
  end
66
72
 
67
73
  failure_message_for_should do |text|
@@ -77,9 +83,9 @@ Spec::Matchers.define :link_to_list_path do |list_path|
77
83
  end
78
84
  end
79
85
 
80
- Spec::Matchers.define :have_autolinked_hashtag do |hashtag|
86
+ RSpec::Matchers.define :have_autolinked_hashtag do |hashtag|
81
87
  match do |text|
82
- @link = Hpricot(text).at("a[@href='http://twitter.com/search?q=#{CGI.escape hashtag}']")
88
+ @link = Nokogiri::HTML(text).search("a[@href='http://twitter.com/search?q=#{CGI.escape hashtag}']")
83
89
  @link &&
84
90
  @link.inner_text &&
85
91
  @link.inner_text == hashtag
data/spec/test_urls.rb CHANGED
@@ -23,12 +23,12 @@ module TestUrls
23
23
  "http://x.com/has/one/char/domain",
24
24
  "http://t.co/nwcLTFF",
25
25
  # "t.co/nwcLTFF"
26
- ]
26
+ ] unless defined?(TestUrls::VALID)
27
27
 
28
28
  INVALID = [
29
29
  "http://no-tld",
30
30
  "http://tld-too-short.x",
31
31
  "http://-doman_dash.com"
32
- ]
32
+ ] unless defined?(TestUrls::INVALID)
33
33
 
34
34
  end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ major, minor, patch = RUBY_VERSION.split('.')
4
+ if major.to_i == 1 && minor.to_i < 9
5
+ describe "base" do
6
+ before do
7
+ $KCODE = 'NONE'
8
+ end
9
+
10
+ after do
11
+ $KCODE = 'u'
12
+ end
13
+
14
+ it "should raise with invalid KCODE on Ruby < 1.9" do
15
+ lambda do
16
+ require 'twitter-text'
17
+ end.should raise_error
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,126 @@
1
+ require 'test/unit'
2
+ require 'yaml'
3
+ $KCODE = 'UTF8'
4
+ require File.dirname(__FILE__) + '/../lib/twitter-text'
5
+
6
+ class ConformanceTest < Test::Unit::TestCase
7
+ include Twitter::Extractor
8
+ include Twitter::Autolink
9
+ include Twitter::HitHighlighter
10
+
11
+ def setup
12
+ @conformance_dir = ENV['CONFORMANCE_DIR'] || File.join(File.dirname(__FILE__), 'twitter-text-conformance')
13
+ end
14
+
15
+ module ExtractorConformance
16
+ def test_replies_extractor_conformance
17
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :replies) do |description, expected, input|
18
+ assert_equal expected, extract_reply_screen_name(input), description
19
+ end
20
+ end
21
+
22
+ def test_mentions_extractor_conformance
23
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :mentions) do |description, expected, input|
24
+ assert_equal expected, extract_mentioned_screen_names(input), description
25
+ end
26
+ end
27
+
28
+ def test_mentions_with_indices_extractor_conformance
29
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :mentions_with_indices) do |description, expected, input|
30
+ expected = expected.map{|elem| elem.symbolize_keys }
31
+ assert_equal expected, extract_mentioned_screen_names_with_indices(input), description
32
+ end
33
+ end
34
+
35
+ def test_url_extractor_conformance
36
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :urls) do |description, expected, input|
37
+ assert_equal expected, extract_urls(input), description
38
+ end
39
+ end
40
+
41
+ def test_urls_with_indices_extractor_conformance
42
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :urls_with_indices) do |description, expected, input|
43
+ expected = expected.map{|elem| elem.symbolize_keys }
44
+ assert_equal expected, extract_urls_with_indices(input), description
45
+ end
46
+ end
47
+
48
+ def test_hashtag_extractor_conformance
49
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :hashtags) do |description, expected, input|
50
+ assert_equal expected, extract_hashtags(input), description
51
+ end
52
+ end
53
+
54
+ def test_hashtags_with_indices_extractor_conformance
55
+ run_conformance_test(File.join(@conformance_dir, 'extract.yml'), :hashtags_with_indices) do |description, expected, input|
56
+ expected = expected.map{|elem| elem.symbolize_keys }
57
+ assert_equal expected, extract_hashtags_with_indices(input), description
58
+ end
59
+ end
60
+ end
61
+ include ExtractorConformance
62
+
63
+ module AutolinkConformance
64
+ def test_users_autolink_conformance
65
+ run_conformance_test(File.join(@conformance_dir, 'autolink.yml'), :usernames) do |description, expected, input|
66
+ assert_equal expected, auto_link_usernames_or_lists(input, :suppress_no_follow => true), description
67
+ end
68
+ end
69
+
70
+ def test_lists_autolink_conformance
71
+ run_conformance_test(File.join(@conformance_dir, 'autolink.yml'), :lists) do |description, expected, input|
72
+ assert_equal expected, auto_link_usernames_or_lists(input, :suppress_no_follow => true), description
73
+ end
74
+ end
75
+
76
+ def test_urls_autolink_conformance
77
+ run_conformance_test(File.join(@conformance_dir, 'autolink.yml'), :urls) do |description, expected, input|
78
+ assert_equal expected, auto_link_urls_custom(input, :suppress_no_follow => true), description
79
+ end
80
+ end
81
+
82
+ def test_hashtags_autolink_conformance
83
+ run_conformance_test(File.join(@conformance_dir, 'autolink.yml'), :hashtags) do |description, expected, input|
84
+ assert_equal expected, auto_link_hashtags(input, :suppress_no_follow => true), description
85
+ end
86
+ end
87
+
88
+ def test_all_autolink_conformance
89
+ run_conformance_test(File.join(@conformance_dir, 'autolink.yml'), :all) do |description, expected, input|
90
+ assert_equal expected, auto_link(input, :suppress_no_follow => true), description
91
+ end
92
+ end
93
+ end
94
+ include AutolinkConformance
95
+
96
+ module HitHighlighterConformance
97
+
98
+ def test_plain_text_conformance
99
+ run_conformance_test(File.join(@conformance_dir, 'hit_highlighting.yml'), :plain_text, true) do |config|
100
+ assert_equal config['expected'], hit_highlight(config['text'], config['hits']), config['description']
101
+ end
102
+ end
103
+
104
+ def test_with_links_conformance
105
+ run_conformance_test(File.join(@conformance_dir, 'hit_highlighting.yml'), :with_links, true) do |config|
106
+ assert_equal config['expected'], hit_highlight(config['text'], config['hits']), config['description']
107
+ end
108
+ end
109
+ end
110
+ include HitHighlighterConformance
111
+
112
+ private
113
+
114
+ def run_conformance_test(file, test_type, hash_config = false, &block)
115
+ yaml = YAML.load_file(file)
116
+ assert yaml["tests"][test_type.to_s], "No such test suite: #{test_type.to_s}"
117
+
118
+ yaml["tests"][test_type.to_s].each do |test_info|
119
+ if hash_config
120
+ yield test_info
121
+ else
122
+ yield test_info['description'], test_info['expected'], test_info['text']
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,23 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = "twitter-text"
3
+ s.version = "1.2.5"
4
+ s.authors = ["Matt Sanford", "Patrick Ewing", "Ben Cherry", "Britt Selvitelle", "Raffi Krikorian"]
5
+ s.email = ["matt@twitter.com", "patrick.henry.ewing@gmail.com", "bcherry@gmail.com", "bs@brittspace.com", "raffi@twitter.com"]
6
+ s.homepage = "http://twitter.com"
7
+ s.description = s.summary = "A gem that provides text handling for Twitter"
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+ s.has_rdoc = true
11
+ s.summary = "Twitter text handling library"
12
+
13
+ s.add_development_dependency "nokogiri"
14
+ s.add_development_dependency "rake"
15
+ s.add_development_dependency "rspec"
16
+ s.add_development_dependency "simplecov"
17
+ s.add_runtime_dependency "actionpack"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitter-text
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 2
9
- - 4
10
- version: 1.2.4
9
+ - 5
10
+ version: 1.2.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Sanford
@@ -15,15 +15,15 @@ authors:
15
15
  - Ben Cherry
16
16
  - Britt Selvitelle
17
17
  - Raffi Krikorian
18
- autorequire: ""
18
+ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2010-11-03 00:00:00 -07:00
22
+ date: 2010-11-18 00:00:00 -08:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
26
- name: actionpack
26
+ name: nokogiri
27
27
  prerelease: false
28
28
  requirement: &id001 !ruby/object:Gem::Requirement
29
29
  none: false
@@ -34,8 +34,64 @@ dependencies:
34
34
  segments:
35
35
  - 0
36
36
  version: "0"
37
- type: :runtime
37
+ type: :development
38
38
  version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: rake
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: simplecov
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: actionpack
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ type: :runtime
94
+ version_requirements: *id005
39
95
  description: A gem that provides text handling for Twitter
40
96
  email:
41
97
  - matt@twitter.com
@@ -50,6 +106,11 @@ extensions: []
50
106
  extra_rdoc_files: []
51
107
 
52
108
  files:
109
+ - .gitignore
110
+ - .gitmodules
111
+ - .rspec
112
+ - Gemfile
113
+ - Gemfile.lock
53
114
  - LICENSE
54
115
  - README.rdoc
55
116
  - Rakefile
@@ -61,14 +122,19 @@ files:
61
122
  - lib/twitter-text.rb
62
123
  - lib/unicode.rb
63
124
  - lib/validation.rb
125
+ - script/destroy
126
+ - script/generate
64
127
  - spec/autolinking_spec.rb
65
128
  - spec/extractor_spec.rb
66
129
  - spec/hithighlighter_spec.rb
67
130
  - spec/regex_spec.rb
68
131
  - spec/spec_helper.rb
69
132
  - spec/test_urls.rb
133
+ - spec/twitter_text_spec.rb
70
134
  - spec/unicode_spec.rb
71
135
  - spec/validation_spec.rb
136
+ - test/conformance_test.rb
137
+ - twitter-text.gemspec
72
138
  has_rdoc: true
73
139
  homepage: http://twitter.com
74
140
  licenses: []
@@ -103,5 +169,14 @@ rubygems_version: 1.3.7
103
169
  signing_key:
104
170
  specification_version: 3
105
171
  summary: Twitter text handling library
106
- test_files: []
107
-
172
+ test_files:
173
+ - spec/autolinking_spec.rb
174
+ - spec/extractor_spec.rb
175
+ - spec/hithighlighter_spec.rb
176
+ - spec/regex_spec.rb
177
+ - spec/spec_helper.rb
178
+ - spec/test_urls.rb
179
+ - spec/twitter_text_spec.rb
180
+ - spec/unicode_spec.rb
181
+ - spec/validation_spec.rb
182
+ - test/conformance_test.rb