link-preview 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OGRiMDRjOTI2ODE4MTQzZDBlNDdlNjBjZjMzN2Q3NmU4Mjk4N2YxMA==
5
+ data.tar.gz: !binary |-
6
+ NzdhYTkyMmYwNGQwMDAzYzE3NTNjZDdhY2U1ZmRjNTJlZmEzZDU5Mg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NGI4ODQyMjU3MzhhMGQ0MThmYjE3ODk1NjBiNjg3ZTNhMTMwOGUyMDQ2MDgx
10
+ ZTU3NTlmY2E3MDlmYmY5NTk1NTgxNDdjNjliMWM1Mzc4YjEwYTEyOGM3OWYy
11
+ ZjljM2Y0ODUzMTU4Y2U1NTIxNmM0OTNmYWE0OWUwN2ZjZGE2YmI=
12
+ data.tar.gz: !binary |-
13
+ M2UyYjI2ZWM2ODIyYzU1MzMwOTk0NjgyMTA4Y2MxN2ViOTcxNzM0ODI1NGMw
14
+ Zjg1OTgxNmVmMDE4MjE3MWY0OTBiZTFiM2UzNjNiMTk3ZTZhMzJlMWI4N2M4
15
+ MTY4NDJmMzhiYjE5NDdjMmJkOGFkNjI2YTlmMzA5ZWVlYTBkMDc=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in link_preview.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Tomas D'Stefano
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # LinkPreview
2
+
3
+ Link preview feature find open graph or normal meta tags.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'link-preview'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install link-preview
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ page = LinkPreview::Page.new('http://globoesporte.globo.com/futebol/times/corinthians/noticia/2013/06/sheik-cobra-responsabilidade-dos-companheiros-durante-folga.html')
23
+
24
+ # Return the title of the page
25
+ #
26
+ page.title
27
+
28
+ # Return the description of the page.
29
+ #
30
+ page.description
31
+
32
+ # Return all images from page
33
+ #
34
+ page.images
35
+
36
+ # Return the favicon link
37
+ #
38
+ page.favicon
39
+
40
+ # Return the uri
41
+ #
42
+ page.uri
43
+ ```
44
+
45
+ Obs.: The Page class is compatible with Active Model Serializers, :).
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ require 'link_preview/version'
2
+ require 'httparty'
3
+ require 'nokogiri'
4
+ require 'active_support/core_ext/object'
5
+
6
+ module LinkPreview
7
+ autoload :ExternalDescription, 'link_preview/external_description'
8
+ autoload :ExternalFavicon, 'link_preview/external_favicon'
9
+ autoload :ExternalImage, 'link_preview/external_image'
10
+ autoload :ExternalResource, 'link_preview/external_resource'
11
+ autoload :ExternalTitle, 'link_preview/external_title'
12
+ autoload :Parser, 'link_preview/parser'
13
+ autoload :Page, 'link_preview/page'
14
+ end
@@ -0,0 +1,23 @@
1
+ module LinkPreview
2
+ class ExternalDescription < ExternalResource
3
+ attr_reader :document
4
+
5
+ def initialize(document)
6
+ @document = document
7
+
8
+ super(text.to_s)
9
+ end
10
+
11
+ def text
12
+ open_graph_description || meta_description
13
+ end
14
+
15
+ def open_graph_description
16
+ find_value(document.css("*[property~='og:description']").first)
17
+ end
18
+
19
+ def meta_description
20
+ find_value(document.css("*[name~='description']").first)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module LinkPreview
2
+ class ExternalFavicon < ExternalImage
3
+ attr_reader :uri, :document, :favicon_element, :itemprop_element, :open_graph_element
4
+
5
+ def initialize(uri, document)
6
+ @uri = uri
7
+ @document = document
8
+
9
+ super(uri, favicon_path.to_s)
10
+ end
11
+
12
+ def favicon_path
13
+ find_value(open_graph_element) || favicon_element['href'] || find_value(itemprop_element)
14
+ end
15
+
16
+ def open_graph_element
17
+ @open_graph_element ||= document.css("*[property~='og:image']").first
18
+ end
19
+
20
+ def favicon_element
21
+ @favicon_element ||= document.css("*[rel~='icon']").first || {}
22
+ end
23
+
24
+ def itemprop_element
25
+ @itemprop_element ||= document.css("*[itemprop~='image']").first
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module LinkPreview
2
+ class ExternalImage < ExternalResource
3
+ attr_reader :image_path
4
+
5
+ def self.parse(uri, document)
6
+ document.css("img").collect { |image|
7
+ self.new(uri, image.attributes['src'].value) if image.attributes['src'].present?
8
+ }
9
+ end
10
+
11
+ def initialize(uri, image_path)
12
+ @uri = uri
13
+ @image_path = image_path
14
+
15
+ super(path)
16
+ end
17
+
18
+ def path
19
+ return '' if image_path.blank?
20
+
21
+ if URI.regexp !~ image_path
22
+ "#{File.join(@uri, image_path)}"
23
+ else
24
+ image_path
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module LinkPreview
2
+ class ExternalResource < String
3
+ def find_value(element)
4
+ element.attributes['content'] if element.present?
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module LinkPreview
2
+ class ExternalTitle < ExternalResource
3
+ def initialize(document)
4
+ @document = document
5
+
6
+ super(text)
7
+ end
8
+
9
+ def text
10
+ find_value(@document.css("*[property~='og:title']").first) || @document.css('title').text
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module LinkPreview
2
+ class Page < Parser
3
+ end
4
+ end
@@ -0,0 +1,76 @@
1
+ module LinkPreview
2
+ class Parser < Hash
3
+ attr_reader :document, :title, :images, :uri
4
+
5
+ def initialize(uri)
6
+ @uri = uri
7
+
8
+ super()
9
+ end
10
+
11
+ # Parse title, favicon and images from an external page.
12
+ #
13
+ def parse!
14
+ merge!(link: @uri, title: title, favicon: favicon, images: images, type: type, description: description)
15
+ rescue StandardError => exception
16
+ merge!(error: exception.message)
17
+ end
18
+
19
+ # @return [True, False] return false if self contain error node. True otherwise.
20
+ #
21
+ def valid?
22
+ self[:error].blank?
23
+ end
24
+
25
+ # Return the title from HTML document.
26
+ #
27
+ # @return [String]
28
+ #
29
+ def title
30
+ LinkPreview::ExternalTitle.new(document)
31
+ end
32
+
33
+ # Return the description from HTML document.
34
+ #
35
+ # @return [String]
36
+ #
37
+ def description
38
+ LinkPreview::ExternalDescription.new(document)
39
+ end
40
+
41
+ # For now all links are considering external.
42
+ # In the future this could be: external, youtube, images.
43
+ #
44
+ def type
45
+ 'external'
46
+ end
47
+
48
+ # Return the absolute path from a favicon element.
49
+ #
50
+ # @return [String]
51
+ #
52
+ def favicon
53
+ @favicon ||= LinkPreview::ExternalFavicon.new(uri, document)
54
+ end
55
+
56
+ # Return all images from the page.
57
+ #
58
+ # @return [Array]
59
+ #
60
+ def images
61
+ @images ||= LinkPreview::ExternalImage.parse(uri, document).unshift(favicon).reject(&:blank?)
62
+ end
63
+
64
+ # Convenience method to be used in the active model serializers.
65
+ #
66
+ def read_attribute_for_serialization(attribute)
67
+ self[attribute.to_sym]
68
+ end
69
+
70
+ private
71
+
72
+ def document
73
+ @document ||= Nokogiri::HTML(HTTParty.get(@uri))
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module LinkPreview
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'link_preview/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "link-preview"
8
+ spec.version = LinkPreview::VERSION
9
+ spec.authors = ["Tomas D'Stefano"]
10
+ spec.email = ["tomas_stefano@successoft.com"]
11
+ spec.description = %q{Link Preview parser feature.}
12
+ spec.summary = %q{Link Preview parser feature.}
13
+ spec.homepage = "https://github.com/tomas-stefano/link_preview"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'httparty'
22
+ spec.add_dependency 'nokogiri'
23
+ spec.add_dependency 'activesupport'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+
28
+ spec.add_development_dependency 'rspec'
29
+ end
@@ -0,0 +1,229 @@
1
+ require 'spec_helper'
2
+
3
+ module LinkPreview
4
+ describe Parser do
5
+ let(:html) do
6
+ %{<html>
7
+ <head>
8
+ <title>Foobar</title>
9
+ <link rel='icon' href='/favicon.ico'>
10
+ </head>
11
+ <body>
12
+ <p>Hello World</p>
13
+ <a href='/foo' rel='nofollow'>Foo</a>
14
+ </body>
15
+ </html>
16
+ }
17
+ end
18
+ let(:link_preview_parser) { Parser.new('http://foobar.com') }
19
+
20
+ before { HTTParty.stub(:get).and_return(html) }
21
+
22
+ describe '#title' do
23
+ context 'when title is nil' do
24
+ let(:html) { '<html><head></head><body><p>Hello World</p></body></html>' }
25
+ subject { link_preview_parser.title }
26
+
27
+ it { should eq '' }
28
+ end
29
+
30
+ context 'when the page had title' do
31
+ subject { link_preview_parser.title }
32
+
33
+ it { should eq 'Foobar' }
34
+ end
35
+ end
36
+
37
+ describe '#favicon' do
38
+ context 'when had favicon' do
39
+ subject { link_preview_parser.favicon }
40
+
41
+ it { should eq 'http://foobar.com/favicon.ico' }
42
+ end
43
+
44
+ context 'when had other implementation of favicon' do
45
+ let(:html) do
46
+ %{<html>
47
+ <head>
48
+ <title>Google</title>
49
+ <meta itemprop="image" content="/images/google_favicon_128.png">
50
+ </head>
51
+ <body>
52
+ <p>Google Search</p>
53
+ </body>
54
+ </html>
55
+ }
56
+ end
57
+ subject { Parser.new('http://google.com').favicon }
58
+
59
+ it { should eq 'http://google.com/images/google_favicon_128.png' }
60
+ end
61
+
62
+ context 'when favicon is a link to other domain' do
63
+ let(:html) do
64
+ %{<html>
65
+ <head>
66
+ <title>Globo</title>
67
+ <link rel="icon" href="http://s.glbimg.com/es/ge/static/globoesporte/img/favicon.png">
68
+ </head>
69
+ <body>
70
+ <p>Google Search</p>
71
+ </body>
72
+ </html>
73
+ }
74
+ end
75
+ subject { Parser.new('http://globoesporte.com').favicon }
76
+
77
+ it { should eq 'http://s.glbimg.com/es/ge/static/globoesporte/img/favicon.png' }
78
+ end
79
+
80
+ context 'when do not had favicon' do
81
+ let(:html) { '<html><head></head><body><p>Hello World</p></body></html>' }
82
+ subject { link_preview_parser.favicon }
83
+
84
+ it { should eq '' }
85
+ end
86
+ end
87
+
88
+ describe '#images' do
89
+ subject { link_preview_parser.images }
90
+
91
+ context 'when contain images from domain' do
92
+ let(:html) do
93
+ %{<html>
94
+ <head>
95
+ <title>Foobar</title>
96
+ </head>
97
+ <body>
98
+ <p>Hello World</p>
99
+ <img src='/foo.png' />
100
+ </body>
101
+ </html>
102
+ }
103
+ end
104
+
105
+ it { should eq ['http://foobar.com/foo.png'] }
106
+ end
107
+
108
+ context 'when contain images from the request domain' do
109
+ subject { Parser.new('trello.com').images }
110
+
111
+ let(:html) do
112
+ %{<html>
113
+ <head>
114
+ <title>Foobar</title>
115
+ </head>
116
+ <body>
117
+ <p>Hello World</p>
118
+ <img src='/foo.png' />
119
+ </body>
120
+ </html>
121
+ }
122
+ end
123
+
124
+ it { should eq ['trello.com/foo.png'] }
125
+ end
126
+
127
+ context 'when contain images from other domain' do
128
+ let(:html) do
129
+ %{<html>
130
+ <head>
131
+ <title>Foobar</title>
132
+ </head>
133
+ <body>
134
+ <p>Hello World</p>
135
+ <img src='http://youtube.com/foo.png' />
136
+ </body>
137
+ </html>
138
+ }
139
+ end
140
+
141
+ it { should eq ['http://youtube.com/foo.png'] }
142
+ end
143
+
144
+ context 'when contain images without src attribute' do
145
+ let(:html) do
146
+ %{<html>
147
+ <head>
148
+ <title>Foobar</title>
149
+ </head>
150
+ <body>
151
+ <p>Hello World</p>
152
+ <img />
153
+ </body>
154
+ </html>
155
+ }
156
+ end
157
+
158
+ it { should eq [] }
159
+ end
160
+ end
161
+
162
+ describe '#parse!' do
163
+ context 'merge the properties' do
164
+ let(:html) do
165
+ %{<html>
166
+ <head>
167
+ <title>GitHub - Build software better, together.</title>
168
+ <link rel='icon' href='/favicon.ico'>
169
+ </head>
170
+ <body>
171
+ <p>Hello World</p>
172
+ <img src="https://a248.e.akamai.net/assets.github.com/images/network.png?1355422571" />
173
+ </body>
174
+ </html>
175
+ }
176
+ end
177
+ subject { Parser.new('github.com').parse! }
178
+
179
+ it { should eq({ link: 'github.com', type: 'external', title: 'GitHub - Build software better, together.', favicon: 'github.com/favicon.ico', images: ['github.com/favicon.ico', 'https://a248.e.akamai.net/assets.github.com/images/network.png?1355422571'], description: ''}) }
180
+ end
181
+
182
+ context 'when raises exception trying to access the page' do
183
+ subject { link_preview_parser.parse! }
184
+ before { HTTParty.should_receive(:get).and_raise(Errno::ECONNREFUSED) }
185
+
186
+ it { should eq({ error: 'Connection refused' }) }
187
+ end
188
+
189
+ context 'with open graph content' do
190
+ let(:html) do
191
+ %{
192
+ <html>
193
+ <head>
194
+ <meta property="og:title" content="globoesporte.com">
195
+ <meta property="og:type" content="website">
196
+ <meta property="og:url" content="globoesporte.globo.com">
197
+ <meta property="og:image" content="http://s.glbimg.com/es/ge/static/globoesporte/img/logo-globoesporte-facebook.jpg">
198
+ <meta property="og:site_name" content="globoesporte.com">
199
+ <meta property="og:description" content="globoesporte.com a melhor cobertura sobre o Futebol.">
200
+ </head>
201
+ </html>
202
+ }
203
+ end
204
+ subject { Parser.new('globoesporte.com') }
205
+ before { subject.parse! }
206
+
207
+ its([:title]) { should eq 'globoesporte.com' }
208
+ its([:description]) { should eq 'globoesporte.com a melhor cobertura sobre o Futebol.' }
209
+ its([:images]) { should eq ['http://s.glbimg.com/es/ge/static/globoesporte/img/logo-globoesporte-facebook.jpg'] }
210
+ end
211
+ end
212
+
213
+ describe '#valid?' do
214
+ subject { Parser.new('http://github.com') }
215
+
216
+ context 'when parse page' do
217
+ before { subject[:link] = 'foo' }
218
+
219
+ it { should be_valid }
220
+ end
221
+
222
+ context 'when raises error parsing page' do
223
+ before { subject[:error] = 'SSL_connect state=SSLv3 bad ecpoint' }
224
+
225
+ it { should_not be_valid }
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1 @@
1
+ require 'link_preview'
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: link-preview
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Tomas D'Stefano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Link Preview parser feature.
98
+ email:
99
+ - tomas_stefano@successoft.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - Gemfile
106
+ - LICENSE.txt
107
+ - README.md
108
+ - Rakefile
109
+ - lib/link_preview.rb
110
+ - lib/link_preview/external_description.rb
111
+ - lib/link_preview/external_favicon.rb
112
+ - lib/link_preview/external_image.rb
113
+ - lib/link_preview/external_resource.rb
114
+ - lib/link_preview/external_title.rb
115
+ - lib/link_preview/page.rb
116
+ - lib/link_preview/parser.rb
117
+ - lib/link_preview/version.rb
118
+ - link_preview.gemspec
119
+ - spec/link_preview/parser_spec.rb
120
+ - spec/spec_helper.rb
121
+ homepage: https://github.com/tomas-stefano/link_preview
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Link Preview parser feature.
145
+ test_files:
146
+ - spec/link_preview/parser_spec.rb
147
+ - spec/spec_helper.rb