link_thumbnailer 0.0.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Ruby gem generating image thumbnails from a given URL. Rank them and give you back an object containing images and website informations. Works like Facebook link previewer.
4
4
 
5
+ Demo Application is [here](https://github.com/gottfrois/link_thumbnailer_demo) !
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -26,6 +28,9 @@ This will add `link_thumbnailer.rb` to `config/initializers/`. See [#Configurati
26
28
 
27
29
  Run `irb` and require the gem:
28
30
 
31
+ require 'rails'
32
+ => true
33
+
29
34
  require 'link_thumbnailer'
30
35
  => true
31
36
 
@@ -64,14 +69,11 @@ Now with a regular website with no particular protocol:
64
69
  object.images.first.source_url
65
70
  => #<URI::HTTP:0x007ff7a923ef58 URL:http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg>
66
71
 
67
- object.images.first.to_json
68
- => "{\"source_url\":\"http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg\",\"mime_type\":\"image/jpeg\",\"density\":\"72x72\",\"rows\":200,\"filesize\":46501,\"number_colors\":9490}"
72
+ object.to_hash
73
+ => {"url"=>"http://foo.com", "images"=>[{:source_url=>"http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg", :mime_type=>"image/jpeg", :rows=>200, :filesize=>46501, :number_colors=>9490}], "title"=>"Foo.com", "description"=>nil}
69
74
 
70
- object.images.first.to_a
71
- => {:source_url=>#<URI::HTTP:0x007f8a4a2561c0 URL:http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg>, :mime_type=>"image/jpeg", :density=>"72x72", :rows=>200, :filesize=>46501, :number_colors=>9490}
72
-
73
- object.images.map(&:to_a)
74
- => [{:source_url=>#<URI::HTTP:0x007f8a4a2561c0 URL:http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg>, :mime_type=>"image/jpeg", :density=>"72x72", :rows=>200, :filesize=>46501, :number_colors=>9490}]
75
+ object.to_json
76
+ => "{\"url\":\"http://foo.com\",\"images\":[{\"source_url\":\"http://foo.com/media/BAhbB1sHOgZmSSItMjAxMi8wNC8yNi8yMC8xMS80OS80MjYvY29yZG92YWJlYWNoLmpwZwY6BkVUWwg6BnA6CnRodW1iSSINNzUweDIwMCMGOwZU/cordovabeach.jpg\",\"mime_type\":\"image/jpeg\",\"rows\":200,\"filesize\":46501,\"number_colors\":9490}],\"title\":\"Foo.com\",\"description\":null}"
75
77
 
76
78
  You can check whether this object is valid or not (set mandatory attributes in the initializer, defaults are `[url, title, images]`)
77
79
 
@@ -81,6 +83,29 @@ You can check whether this object is valid or not (set mandatory attributes in t
81
83
  You also can set `limit` and `top` options at runtime:
82
84
 
83
85
  object = LinkThumbnailer.generate('http://foo.com', :top => 10, :limit => 20)
86
+
87
+ ## Preview Controller
88
+
89
+ For an easy integration into your application, use the builtin `PreviewController`.
90
+
91
+ Take a look at the demo application [here](https://github.com/gottfrois/link_thumbnailer_demo).
92
+
93
+ Basically, all you have to do in your view is something like this:
94
+
95
+ <%= form_tag '/link/preview', :method => 'post', :remote => true do %>
96
+ <%= text_field_tag :url %>
97
+ <%= submit_tag 'Preview' %>
98
+ <% end %>
99
+
100
+ Don't forget to add this anywhere in your `routes.rb` file:
101
+
102
+ use_link_thumbnailer
103
+
104
+ Note: You won't have to bother with this if you did runned the installer using:
105
+
106
+ $ rails g link_thumbnailer:install
107
+
108
+ The `PreviewController` will automatically respond to json calls, returning json version of the preview object. Just like in the IRB console above.
84
109
 
85
110
  ## Configuration
86
111
 
@@ -120,6 +145,7 @@ Implemented:
120
145
  - Find images and sort them according to how well they represent what the page is about (includes absolute images).
121
146
  - Sort images based on their size and color.
122
147
  - Blacklist some well known advertisings image urls.
148
+ - Routes and Controllers to handle preview generation
123
149
 
124
150
  Coming soon:
125
151
 
@@ -0,0 +1,4 @@
1
+ module LinkThumbnailer
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module LinkThumbnailer
2
+ class PreviewsController < ApplicationController
3
+
4
+ respond_to :json
5
+
6
+ def create
7
+ @preview = LinkThumbnailer.generate(params[:url])
8
+ render :json => @preview.to_json
9
+ end
10
+ end
11
+ end
@@ -6,6 +6,10 @@ module LinkThumbnailer
6
6
 
7
7
  desc 'Creates a LinkThumbnailer initializer for your application.'
8
8
 
9
+ def install
10
+ route "use_link_thumbnailer"
11
+ end
12
+
9
13
  def copy_initializer
10
14
  template 'initializer.rb', 'config/initializers/link_thumbnailer.rb'
11
15
  end
@@ -18,6 +18,12 @@ LinkThumbnailer.configure do |config|
18
18
  # %r{^http://s7\.addthis\.com/}
19
19
  # ]
20
20
 
21
+ # Included Rmagick attributes for images. See http://www.imagemagick.org/RMagick/doc/
22
+ # for more details.
23
+ # 'source_url' is a custom attribute and should always be included since this
24
+ # is where you'll find the image url.
25
+ # config.rmagick_attributes = %w(source_url mime_type colums rows filesize number_colors)
26
+
21
27
  # Fetch 10 images maximum.
22
28
  # config.limit = 10
23
29
 
@@ -0,0 +1,9 @@
1
+ module LinkThumbnailer
2
+ class Engine < Rails::Engine
3
+
4
+ initializer 'link_thumbnailer.routes' do
5
+ LinkThumbnailer::Rails::Routes.install!
6
+ end
7
+
8
+ end
9
+ end
@@ -1,4 +1,5 @@
1
1
  require 'hashie'
2
+ require 'json'
2
3
 
3
4
  module LinkThumbnailer
4
5
  class Object < Hashie::Mash
@@ -20,5 +21,21 @@ module LinkThumbnailer
20
21
  true
21
22
  end
22
23
 
24
+ def to_hash
25
+ if self.images.none? {|i| i.is_a?(String)}
26
+ super.merge('images' => self.images.map(&:to_hash))
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def to_json
33
+ if self.images.none? {|i| i.is_a?(String)}
34
+ JSON.generate(self.to_hash.merge('images' => self.images.map(&:to_hash)))
35
+ else
36
+ JSON.generate(self.to_hash)
37
+ end
38
+ end
39
+
23
40
  end
24
41
  end
@@ -7,7 +7,11 @@ module LinkThumbnailer
7
7
  object[$1.gsub('-', '_')] = m.attribute('content').to_s
8
8
  end
9
9
  end
10
- object[:images] = [object[:image]] if object[:image]
10
+
11
+ object[:images] = []
12
+ if object[:image]
13
+ object[:images] << { :source_url => object[:image] }
14
+ end
11
15
 
12
16
  object
13
17
  end
@@ -0,0 +1,30 @@
1
+ module LinkThumbnailer
2
+ module Rails
3
+ class Routes
4
+ class Mapper
5
+
6
+ def initialize(mapping = Mapping.new)
7
+ @mapping = mapping
8
+ end
9
+
10
+ def map(&block)
11
+ self.instance_eval(&block) if block
12
+ @mapping
13
+ end
14
+
15
+ def controllers(controller_names = {})
16
+ @mapping.controllers.merge!(controller_names)
17
+ end
18
+
19
+ def skip_controllers(*controller_names)
20
+ @mapping.skips = controller_names
21
+ end
22
+
23
+ def as(alias_names = {})
24
+ @mapping.as.merge!(alias_names)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ module LinkThumbnailer
2
+ module Rails
3
+ class Routes
4
+ class Mapping
5
+
6
+ attr_accessor :controllers, :as, :skips
7
+
8
+ def initialize
9
+ @controllers = {
10
+ :previews => 'link_thumbnailer/previews'
11
+ }
12
+
13
+ @as = {
14
+ :previews => :preview
15
+ }
16
+
17
+ @skips = []
18
+ end
19
+
20
+ def [](routes)
21
+ {
22
+ :controllers => @controllers[routes],
23
+ :as => @as[routes]
24
+ }
25
+ end
26
+
27
+ def skipped?(controller)
28
+ return @skips.include?(controller)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,47 @@
1
+ require 'link_thumbnailer/rails/routes/mapping'
2
+ require 'link_thumbnailer/rails/routes/mapper'
3
+
4
+ module LinkThumbnailer
5
+ module Rails
6
+ class Routes
7
+
8
+ module Helper
9
+ def use_link_thumbnailer(options = {}, &block)
10
+ LinkThumbnailer::Rails::Routes.new(self, &block).generate_routes!(options)
11
+ end
12
+ end
13
+
14
+ def self.install!
15
+ ActionDispatch::Routing::Mapper.send :include, LinkThumbnailer::Rails::Routes::Helper
16
+ end
17
+
18
+ attr_accessor :routes
19
+
20
+ def initialize(routes, &options)
21
+ @routes, @options = routes, options
22
+ end
23
+
24
+ def generate_routes!(options)
25
+ @mapping = Mapper.new.map(&@options)
26
+ routes.scope 'link', :as => 'link' do
27
+ map_route(:previews, :preview_routes)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def map_route(name, method)
34
+ unless @mapping.skipped?(name)
35
+ send method, @mapping[name]
36
+ end
37
+ end
38
+
39
+ def preview_routes(mapping)
40
+ routes.scope :controller => mapping[:controllers] do
41
+ routes.match 'preview', :via => :post, :action => :create, :as => mapping[:as], :defaults => { :format => 'json' }
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module LinkThumbnailer
2
- VERSION = "0.0.6"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,22 +1,18 @@
1
- require 'json'
2
-
3
1
  module LinkThumbnailer
4
2
  module WebImage
5
3
 
6
4
  attr_accessor :source_url, :doc
7
5
 
8
- def to_a
6
+ def to_hash
9
7
  result = {}
10
- %w(source_url mime_type density colums rows filesize number_colors).each {|m|
11
- result[m.to_sym] = self.send(m) if self.respond_to?(m)
8
+ LinkThumbnailer.configuration.rmagick_attributes.each {|m|
9
+ k = m.to_sym
10
+ result[k] = self.send(m) if self.respond_to?(m)
11
+ result[k] = result[k].to_s if result[k].is_a?(URI)
12
12
  }
13
13
 
14
14
  result
15
15
  end
16
16
 
17
- def to_json
18
- JSON(self.to_a)
19
- end
20
-
21
17
  end
22
18
  end
@@ -1,3 +1,4 @@
1
+ require 'link_thumbnailer/engine' if defined? Rails
1
2
  require 'link_thumbnailer/config'
2
3
  require 'link_thumbnailer/object'
3
4
  require 'link_thumbnailer/fetcher'
@@ -7,13 +8,15 @@ require 'link_thumbnailer/img_url_filter'
7
8
  require 'link_thumbnailer/img_parser'
8
9
  require 'link_thumbnailer/img_comparator'
9
10
  require 'link_thumbnailer/web_image'
10
-
11
11
  require 'link_thumbnailer/opengraph'
12
-
13
12
  require 'link_thumbnailer/version'
14
13
 
15
14
  module LinkThumbnailer
16
15
 
16
+ module Rails
17
+ autoload :Routes, 'link_thumbnailer/rails/routes'
18
+ end
19
+
17
20
  class << self
18
21
 
19
22
  attr_accessor :configuration, :object, :fetcher, :doc_parser,
@@ -30,6 +33,7 @@ module LinkThumbnailer
30
33
  %r{^http://pixel\.quantserve\.com/},
31
34
  %r{^http://s7\.addthis\.com/}
32
35
  ],
36
+ :rmagick_attributes => %w(source_url mime_type colums rows filesize number_colors),
33
37
  :limit => 10,
34
38
  :top => 5
35
39
  )
@@ -48,6 +52,8 @@ module LinkThumbnailer
48
52
  self.object = LinkThumbnailer::Object.new
49
53
  self.fetcher = LinkThumbnailer::Fetcher.new
50
54
  self.doc_parser = LinkThumbnailer::DocParser.new
55
+ self.img_url_filters = [LinkThumbnailer::ImgUrlFilter.new]
56
+ self.img_parser = LinkThumbnailer::ImgParser.new(self.fetcher, self.img_url_filters)
51
57
 
52
58
  doc_string = self.fetcher.fetch(url)
53
59
  doc = self.doc_parser.parse(doc_string, url)
@@ -56,11 +62,10 @@ module LinkThumbnailer
56
62
 
57
63
  # Try Opengraph first
58
64
  self.object = LinkThumbnailer::Opengraph.parse(self.object, doc)
65
+
59
66
  return self.object if self.object.valid?
60
67
 
61
68
  # Else try manually
62
- self.img_url_filters = [LinkThumbnailer::ImgUrlFilter.new]
63
- self.img_parser = LinkThumbnailer::ImgParser.new(self.fetcher, self.img_url_filters)
64
69
 
65
70
  self.object[:title] = doc.title
66
71
  self.object[:description] = doc.description
@@ -19,7 +19,7 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency(%q{hashie}, ['~> 1.2.0'])
20
20
  gem.add_dependency(%q{net-http-persistent}, ['~> 2.7'])
21
21
  gem.add_dependency(%q{rmagick}, ['~> 2.13.1'])
22
- gem.add_dependency(%q{json}, ['~> 1.7.5'])
22
+ gem.add_dependency(%q{json}, ['~> 1.7.6'])
23
23
 
24
24
  gem.add_development_dependency(%q{rspec}, ['~> 2.11.0'])
25
25
  gem.add_development_dependency(%q{webmock}, ['~> 1.8.10'])
@@ -25,6 +25,7 @@ describe LinkThumbnailer do
25
25
  config.strict = false
26
26
  config.redirect_limit = 5
27
27
  config.blacklist_urls = []
28
+ config.rmagick_attributes = []
28
29
  config.limit = 5
29
30
  config.top = 10
30
31
  }
@@ -38,6 +39,7 @@ describe LinkThumbnailer do
38
39
  specify { LinkThumbnailer.configuration.strict.should be_false }
39
40
  specify { LinkThumbnailer.configuration.redirect_limit.should eq(5) }
40
41
  specify { LinkThumbnailer.configuration.blacklist_urls.should eq([]) }
42
+ specify { LinkThumbnailer.configuration.rmagick_attributes.should eq([]) }
41
43
  specify { LinkThumbnailer.configuration.limit.should eq(5) }
42
44
  specify { LinkThumbnailer.configuration.top.should eq(10) }
43
45
 
@@ -60,6 +62,7 @@ describe LinkThumbnailer do
60
62
  %r{^http://pixel\.quantserve\.com/},
61
63
  %r{^http://s7\.addthis\.com/}
62
64
  ]) }
65
+ specify { LinkThumbnailer.configuration.rmagick_attributes.should eq(%w(source_url mime_type colums rows filesize number_colors)) }
63
66
  specify { LinkThumbnailer.configuration.limit.should eq(10) }
64
67
  specify { LinkThumbnailer.configuration.top.should eq(5) }
65
68
 
data/spec/object_spec.rb CHANGED
@@ -3,6 +3,8 @@ require 'spec_helper'
3
3
  describe LinkThumbnailer::Object do
4
4
 
5
5
  it { should respond_to :valid? }
6
+ it { should respond_to :to_hash }
7
+ it { should respond_to :to_json }
6
8
 
7
9
  let(:object) { LinkThumbnailer::Object.new }
8
10
 
@@ -92,4 +94,37 @@ describe LinkThumbnailer::Object do
92
94
 
93
95
  end
94
96
 
97
+ context "dealing with WebImage module" do
98
+
99
+ class Foo
100
+ end
101
+
102
+ let(:foo) { Foo.new }
103
+
104
+ before do
105
+ foo.extend LinkThumbnailer::WebImage
106
+ object['images'] = [foo]
107
+ end
108
+
109
+ describe ".to_hash" do
110
+
111
+ subject { object.to_hash }
112
+
113
+ it { subject.is_a?(Hash).should be_true }
114
+ it { should include('images') }
115
+ it { subject['images'].is_a?(Array).should be_true }
116
+
117
+ end
118
+
119
+ describe ".to_json" do
120
+
121
+ subject { object.to_json }
122
+
123
+ it { subject.is_a?(String).should be_true }
124
+ it { should include('images') }
125
+
126
+ end
127
+
128
+ end
129
+
95
130
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'rspec'
2
2
  require 'webmock/rspec'
3
+ require 'json'
3
4
 
4
5
  require 'link_thumbnailer'
5
6
 
6
7
  RSpec.configure do |config|
8
+ config.mock_with :rspec
7
9
  end
@@ -13,26 +13,25 @@ describe LinkThumbnailer::WebImage do
13
13
 
14
14
  subject { foo }
15
15
 
16
- it { should respond_to :to_a }
17
- it { should respond_to :to_json }
16
+ it { should respond_to :to_hash }
18
17
  it { should respond_to :source_url }
19
18
  it { should respond_to :doc }
20
19
 
21
- describe ".to_a" do
20
+ describe ".to_hash" do
22
21
 
23
22
  context "with default attributes" do
24
23
 
25
24
  let(:attributes) { [:source_url] }
26
25
 
27
- subject { foo.to_a }
26
+ subject { foo.to_hash }
28
27
 
29
- it { subject.keys.should eq(attributes) }
28
+ it { subject.keys.should eq(attributes.map(&:to_sym)) }
30
29
 
31
30
  end
32
31
 
33
32
  context "with all attributes" do
34
33
 
35
- let(:attributes) { [:source_url, :mime_type, :density, :colums, :rows, :filesize, :number_colors] }
34
+ let(:attributes) { LinkThumbnailer.configuration.rmagick_attributes }
36
35
 
37
36
  before do
38
37
  attributes.each {|a| foo.class.send(:define_method, a.to_sym) { 'foo' } }
@@ -42,46 +41,13 @@ describe LinkThumbnailer::WebImage do
42
41
  attributes.each {|a| foo.class.send(:undef_method, a.to_sym) }
43
42
  end
44
43
 
45
- subject { foo.to_a }
44
+ subject { foo.to_hash }
46
45
 
47
- it { subject.keys.should eq(attributes) }
46
+ it { subject.keys.should eq(attributes.map(&:to_sym)) }
48
47
  it { subject.values.should include('foo') }
49
48
 
50
49
  end
51
50
 
52
51
  end
53
52
 
54
- describe ".to_json" do
55
-
56
- context "with default attributes" do
57
-
58
- let(:attributes) { [:source_url] }
59
-
60
- subject { foo.to_json }
61
-
62
- it { JSON.parse(subject).keys.map(&:to_sym).should eq(attributes) }
63
-
64
- end
65
-
66
- context "with all attributes" do
67
-
68
- let(:attributes) { [:source_url, :mime_type, :density, :colums, :rows, :filesize, :number_colors] }
69
-
70
- before do
71
- attributes.each {|a| foo.class.send(:define_method, a.to_sym) { 'foo' } }
72
- end
73
-
74
- after do
75
- attributes.each {|a| foo.class.send(:undef_method, a.to_sym) }
76
- end
77
-
78
- subject { foo.to_json }
79
-
80
- it { JSON.parse(subject).keys.map(&:to_sym).should eq(attributes) }
81
- it { JSON.parse(subject).values.should include('foo') }
82
-
83
- end
84
-
85
- end
86
-
87
53
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: link_thumbnailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-27 00:00:00.000000000 Z
12
+ date: 2012-12-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -82,7 +82,7 @@ dependencies:
82
82
  requirements:
83
83
  - - ~>
84
84
  - !ruby/object:Gem::Version
85
- version: 1.7.5
85
+ version: 1.7.6
86
86
  type: :runtime
87
87
  prerelease: false
88
88
  version_requirements: !ruby/object:Gem::Requirement
@@ -90,7 +90,7 @@ dependencies:
90
90
  requirements:
91
91
  - - ~>
92
92
  - !ruby/object:Gem::Version
93
- version: 1.7.5
93
+ version: 1.7.6
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: rspec
96
96
  requirement: !ruby/object:Gem::Requirement
@@ -137,18 +137,24 @@ files:
137
137
  - LICENSE
138
138
  - README.md
139
139
  - Rakefile
140
+ - app/controllers/link_thumbnailer/application_controller.rb
141
+ - app/controllers/link_thumbnailer/previews_controller.rb
140
142
  - lib/generators/link_thumbnailer/install_generator.rb
141
143
  - lib/generators/templates/initializer.rb
142
144
  - lib/link_thumbnailer.rb
143
145
  - lib/link_thumbnailer/config.rb
144
146
  - lib/link_thumbnailer/doc.rb
145
147
  - lib/link_thumbnailer/doc_parser.rb
148
+ - lib/link_thumbnailer/engine.rb
146
149
  - lib/link_thumbnailer/fetcher.rb
147
150
  - lib/link_thumbnailer/img_comparator.rb
148
151
  - lib/link_thumbnailer/img_parser.rb
149
152
  - lib/link_thumbnailer/img_url_filter.rb
150
153
  - lib/link_thumbnailer/object.rb
151
154
  - lib/link_thumbnailer/opengraph.rb
155
+ - lib/link_thumbnailer/rails/routes.rb
156
+ - lib/link_thumbnailer/rails/routes/mapper.rb
157
+ - lib/link_thumbnailer/rails/routes/mapping.rb
152
158
  - lib/link_thumbnailer/version.rb
153
159
  - lib/link_thumbnailer/web_image.rb
154
160
  - link_thumbnailer.gemspec