middleman-thumbnailer 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +19 -0
  4. data/Dockerfile +17 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +21 -0
  7. data/README.md +42 -0
  8. data/Rakefile +15 -0
  9. data/docker-compose.yml +7 -0
  10. data/features/namespace.feature +15 -0
  11. data/features/step_definitions/server_steps.rb +46 -0
  12. data/features/support/env.rb +8 -0
  13. data/features/thumbnails.feature +24 -0
  14. data/fixtures/namespace/config.rb +77 -0
  15. data/fixtures/namespace/source/images/background.png +0 -0
  16. data/fixtures/namespace/source/images/middleman.png +0 -0
  17. data/fixtures/namespace/source/images/test/background.png +0 -0
  18. data/fixtures/namespace/source/images/test1/background.png +0 -0
  19. data/fixtures/namespace/source/index.html.erb +10 -0
  20. data/fixtures/namespace/source/javascripts/all.js +1 -0
  21. data/fixtures/namespace/source/layouts/layout.erb +19 -0
  22. data/fixtures/namespace/source/stylesheets/all.css +55 -0
  23. data/fixtures/namespace/source/stylesheets/normalize.css +375 -0
  24. data/fixtures/thumbnails/config.rb +76 -0
  25. data/fixtures/thumbnails/source/images/background.png +0 -0
  26. data/fixtures/thumbnails/source/images/middleman.png +0 -0
  27. data/fixtures/thumbnails/source/index.html.erb +10 -0
  28. data/fixtures/thumbnails/source/javascripts/all.js +1 -0
  29. data/fixtures/thumbnails/source/layouts/layout.erb +19 -0
  30. data/fixtures/thumbnails/source/stylesheets/all.css +55 -0
  31. data/fixtures/thumbnails/source/stylesheets/normalize.css +375 -0
  32. data/lib/middleman-thumbnailer.rb +8 -0
  33. data/lib/middleman-thumbnailer/extension.rb +192 -0
  34. data/lib/middleman-thumbnailer/thumbnail-generator.rb +78 -0
  35. data/lib/middleman-thumbnailer/version.rb +5 -0
  36. data/lib/middleman_extension.rb +1 -0
  37. data/middleman-thumbnailer.gemspec +24 -0
  38. data/spec/thumbnail-generator_spec.rb +22 -0
  39. metadata +142 -0
@@ -0,0 +1,8 @@
1
+ require "middleman-core"
2
+
3
+ require "middleman-thumbnailer/version"
4
+
5
+ ::Middleman::Extensions.register(:thumbnailer) do
6
+ require "middleman-thumbnailer/extension"
7
+ ::Middleman::Thumbnailer
8
+ end
@@ -0,0 +1,192 @@
1
+ require 'fileutils'
2
+ require 'mime-types'
3
+
4
+ require 'middleman-thumbnailer/thumbnail-generator'
5
+
6
+ module Middleman
7
+ module Thumbnailer
8
+ class << self
9
+
10
+ attr_accessor :options
11
+
12
+ def registered(app, options={})
13
+
14
+ options[:filetypes] ||= [:jpg, :jpeg, :png]
15
+ options[:include_data_thumbnails] = false unless options.has_key? :include_data_thumbnails
16
+ options[:namespace_directory] = ["**"] unless options.has_key? :namespace_directory
17
+
18
+ Thumbnailer.options = options
19
+
20
+ app.helpers Helpers
21
+
22
+ options[:middleman_app] = app
23
+
24
+ app.after_configuration do
25
+
26
+ options[:build_dir] = build_dir
27
+
28
+ #stash the source images dir in options for the Rack middleware
29
+ options[:images_source_dir] = File.join(source_dir, images_dir)
30
+ options[:source_dir] = source_dir
31
+
32
+ dimensions = options[:dimensions]
33
+ namespace = options[:namespace_directory]
34
+
35
+ app.before_build do
36
+ dir = File.join(source_dir, images_dir)
37
+
38
+
39
+ files = DirGlob.glob(dir, namespace, options[:filetypes])
40
+
41
+ files.each do |file|
42
+ path = file.gsub(source_dir, '')
43
+ specs = ThumbnailGenerator.specs(path, dimensions)
44
+ ThumbnailGenerator.generate(source_dir, File.join(root, build_dir), path, specs)
45
+ end
46
+ end
47
+
48
+ sitemap.register_resource_list_manipulator(:thumbnailer, SitemapExtension.new(self), true)
49
+
50
+ app.use Rack, options
51
+ end
52
+ end
53
+ alias :included :registered
54
+ end
55
+
56
+ module Helpers
57
+ def thumbnail_specs(image, name)
58
+ dimensions = Thumbnailer.options[:dimensions]
59
+ ThumbnailGenerator.specs(image, dimensions)
60
+ end
61
+
62
+ def thumbnail_url(image, name, options = {})
63
+ include_images_dir = options.delete :include_images_dir
64
+
65
+ url = thumbnail_specs(image, name)[name][:name]
66
+ url = File.join(images_dir, url) if include_images_dir
67
+
68
+ url
69
+ end
70
+
71
+ def thumbnail(image, name, html_options = {})
72
+ specs_for_data_attribute = thumbnail_specs(image, name).map {|name, spec| "#{name}:#{spec[:name]}"}
73
+
74
+ html_options.merge!({'data-thumbnails' => specs_for_data_attribute.join('|')}) if Thumbnailer.options[:include_data_thumbnails]
75
+
76
+ image_tag(thumbnail_url(image, name), html_options)
77
+ end
78
+ end
79
+
80
+ class DirGlob
81
+ def self.glob(root, namespaces, filetypes)
82
+ filetypes_with_capitals = filetypes.reduce([]) { |memo, file| memo.concat [file, file.upcase] }
83
+ glob_str = "#{root}/{#{namespaces.join(',')}}/**/*.{#{filetypes_with_capitals.join(',')}}"
84
+ Dir[glob_str]
85
+ end
86
+ end
87
+
88
+ class SitemapExtension
89
+ def initialize(app)
90
+ @app = app
91
+ end
92
+
93
+ # Add sitemap resource for every image in the sprockets load path
94
+ def manipulate_resource_list(resources)
95
+
96
+ images_dir_abs = File.join(@app.source_dir, @app.images_dir)
97
+
98
+ images_dir = @app.images_dir
99
+
100
+ options = Thumbnailer.options
101
+ dimensions = options[:dimensions]
102
+ namespace = options[:namespace_directory].join(',')
103
+
104
+ files = DirGlob.glob(images_dir_abs, options[:namespace_directory], options[:filetypes])
105
+
106
+ resource_list = files.map do |file|
107
+ path = file.gsub(@app.source_dir + File::SEPARATOR, '')
108
+ specs = ThumbnailGenerator.specs(path, dimensions)
109
+ specs.map do |name, spec|
110
+ resource = nil
111
+ resource = Middleman::Sitemap::Resource.new(@app.sitemap, spec[:name], File.join(options[:build_dir], spec[:name])) unless name == :original
112
+ end
113
+ end.flatten.reject {|resource| resource.nil? }
114
+
115
+ resources + resource_list
116
+ end
117
+
118
+ end
119
+
120
+
121
+ # Rack middleware to convert images on the fly
122
+ class Rack
123
+
124
+ # Init
125
+ # @param [Class] app
126
+ # @param [Hash] options
127
+ def initialize(app, options={})
128
+ @app = app
129
+ @options = options
130
+
131
+ files = DirGlob.glob(options[:images_source_dir], options[:namespace_directory], options[:filetypes])
132
+
133
+ @original_map = ThumbnailGenerator.original_map_for_files(files, options[:dimensions])
134
+
135
+ end
136
+
137
+ # Rack interface
138
+ # @param [Rack::Environmemt] env
139
+ # @return [Array]
140
+ def call(env)
141
+ status, headers, response = @app.call(env)
142
+
143
+ path = env["PATH_INFO"]
144
+
145
+ absolute_path = File.join(@options[:source_dir], path)
146
+
147
+ root_dir = @options[:middleman_app].root
148
+
149
+ tmp_dir = File.join(root_dir, 'tmp', 'middleman-thumbnail-cache')
150
+
151
+ if original_specs = @original_map[absolute_path]
152
+ thumbnail_path = Pathname.new original_specs[:spec][:name]
153
+ #the name for the generate spec filename is relative to the source dir, remove this
154
+ thumbnail_path = thumbnail_path.relative_path_from Pathname.new(@options[:source_dir])
155
+
156
+ cache_file = File.join tmp_dir, thumbnail_path.to_s
157
+ cache_dir = File.dirname cache_file
158
+ FileUtils.mkdir_p(cache_dir) unless Dir.exist?(cache_dir)
159
+
160
+ file_info = nil
161
+ file_data = nil
162
+
163
+ original_file = original_specs[:original]
164
+ original_mtime = File.mtime original_file
165
+ if File.exist?(cache_file) && (original_mtime == File.mtime(cache_file))
166
+ file_data = IO.read(cache_file)
167
+ file_info = {length: ::Rack::Utils.bytesize(file_data).to_s, mime_type: ::MIME::Types.type_for(cache_file).first.to_s}
168
+ else
169
+ image = ThumbnailGenerator.image_for_spec(original_file, original_specs[:spec])
170
+ file_data = image.to_blob
171
+
172
+ file_info = {length: ::Rack::Utils.bytesize(file_data).to_s, mime_type: image.mime_type}
173
+
174
+ File.open(cache_file, 'wb') do |file|
175
+ file.write file_data
176
+ end
177
+
178
+ File.utime(original_mtime, original_mtime, cache_file)
179
+
180
+ image.destroy!
181
+ end
182
+
183
+ status = 200
184
+ headers["Content-Length"] = file_info[:length]
185
+ headers["Content-Type"] = file_info[:mime_type]
186
+ response = [file_data]
187
+ end
188
+ [status, headers, response]
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,78 @@
1
+ require 'RMagick'
2
+ require 'fileutils'
3
+
4
+ module Middleman
5
+ #actually creates the thumbnail names
6
+ class ThumbnailGenerator
7
+ class << self
8
+
9
+ def specs(origin, dimensions)
10
+ ret = {original: {name: origin}}
11
+ file_parts = origin.split('.')
12
+ basename = file_parts[0..-2].join('.')
13
+ ext = file_parts.last
14
+
15
+ dimensions.each do |name, dimension|
16
+ ret[name] = {name: "#{basename}-#{name}-#{dimension}.#{ext}", dimensions: dimension}
17
+ end
18
+
19
+ ret
20
+ end
21
+
22
+ def generate(source_dir, output_dir, origin, specs)
23
+ origin_absolute = File.join(source_dir, origin)
24
+ yield_images(origin_absolute, specs) do |img, spec|
25
+ output_file = File.join(output_dir, spec[:name])
26
+ origin_mtime = File.mtime origin_absolute
27
+ #FIXME: this sucks & I should be shot, however in before_build, we havend created the build dir
28
+ # therefor we will have to create it here
29
+ output_file_immediate_dir = File.dirname output_file
30
+ FileUtils.mkdir_p output_file_immediate_dir unless Dir.exist? output_file_immediate_dir
31
+ if !File.exist?(output_file) || origin_mtime != File.mtime(output_file) then
32
+ #puts "writing #{output_file}"
33
+ img.write output_file
34
+ end
35
+ File.utime(origin_mtime, origin_mtime, output_file)
36
+ end
37
+ end
38
+
39
+ def yield_images(origin, specs)
40
+ image = ::Magick::Image.read(origin).first
41
+ specs.each do |name, spec|
42
+ if spec.has_key? :dimensions then
43
+ image.change_geometry(spec[:dimensions]) do |cols, rows, img|
44
+ img = img.resize(cols, rows)
45
+ img = img.sharpen(0.5, 0.5)
46
+ yield img, spec
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def image_for_spec(origin, spec)
53
+ image = ::Magick::Image.read(origin).first
54
+
55
+ if spec.has_key? :dimensions then
56
+ image.change_geometry(spec[:dimensions]) do |cols, rows, img|
57
+ img = img.resize(cols, rows)
58
+ img = img.sharpen(0.5, 0.5)
59
+ return img
60
+ end
61
+ end
62
+ return image
63
+ end
64
+
65
+
66
+ #This returns a reverse mapping from a thumbnail's filename to the original filename, and the thumbnail's specs
67
+ def original_map_for_files(files, specs)
68
+ map = files.inject({}) do |memo, file|
69
+ generated_specs = self.specs(file, specs)
70
+ generated_specs.each do |name, spec|
71
+ memo[spec[:name]] = {:original => generated_specs[:original][:name], :spec => spec}
72
+ end
73
+ memo
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ module Middleman
2
+ module Thumbnailer
3
+ VERSION = "0.1.2"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require "middleman-thumbnailer"
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift File.expand_path("./lib")
3
+ require "middleman-thumbnailer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "middleman-thumbnailer"
7
+ s.version = Middleman::Thumbnailer::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Nicholas Hemsley"]
10
+ s.email = ["nick.hems@gmail.com"]
11
+ s.homepage = "https://github.com/nhemsley/middleman-thumbnailer"
12
+ s.licenses = ['MIT']
13
+ s.summary = %q{Generate thumbnail versions of image files}
14
+ s.description = s.summary
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency("middleman", ["~> 3.4.1"])
21
+ s.add_runtime_dependency("rake", ['<= 11.0'])
22
+ s.add_runtime_dependency("rmagick", ["~> 2.16.0"])
23
+ s.add_runtime_dependency("mime-types", ["~> 2.1"])
24
+ end
@@ -0,0 +1,22 @@
1
+ # require 'spec_helper'
2
+ require 'middleman-thumbnailer'
3
+
4
+ require "middleman-thumbnailer/thumbnail-generator"
5
+
6
+ describe ::Middleman::ThumbnailGenerator do
7
+ let(:klass) { Middleman::ThumbnailGenerator }
8
+ let(:filename) {'test.jpg'}
9
+ describe "#specs" do
10
+
11
+ # it "should always return the original filename in :original" do
12
+ # dimensions = {:small => '100x100'}
13
+ # klass.generate(filename, dimensions).should include(:original => filename)
14
+ # end
15
+
16
+ # it "should always return the original filename in :original" do
17
+ # dimensions = {:small => '100x100'}
18
+ # klass.generate(filename, dimensions).should include(:small => 'test-small-100x100.jpg')
19
+ # end
20
+ end
21
+
22
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleman-thumbnailer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Hemsley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: middleman
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "<="
32
+ - !ruby/object:Gem::Version
33
+ version: '11.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "<="
39
+ - !ruby/object:Gem::Version
40
+ version: '11.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rmagick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.16.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.16.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: mime-types
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ description: Generate thumbnail versions of image files
70
+ email:
71
+ - nick.hems@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Dockerfile
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - docker-compose.yml
84
+ - features/namespace.feature
85
+ - features/step_definitions/server_steps.rb
86
+ - features/support/env.rb
87
+ - features/thumbnails.feature
88
+ - fixtures/namespace/config.rb
89
+ - fixtures/namespace/source/images/background.png
90
+ - fixtures/namespace/source/images/middleman.png
91
+ - fixtures/namespace/source/images/test/background.png
92
+ - fixtures/namespace/source/images/test1/background.png
93
+ - fixtures/namespace/source/index.html.erb
94
+ - fixtures/namespace/source/javascripts/all.js
95
+ - fixtures/namespace/source/layouts/layout.erb
96
+ - fixtures/namespace/source/stylesheets/all.css
97
+ - fixtures/namespace/source/stylesheets/normalize.css
98
+ - fixtures/thumbnails/config.rb
99
+ - fixtures/thumbnails/source/images/background.png
100
+ - fixtures/thumbnails/source/images/middleman.png
101
+ - fixtures/thumbnails/source/index.html.erb
102
+ - fixtures/thumbnails/source/javascripts/all.js
103
+ - fixtures/thumbnails/source/layouts/layout.erb
104
+ - fixtures/thumbnails/source/stylesheets/all.css
105
+ - fixtures/thumbnails/source/stylesheets/normalize.css
106
+ - lib/middleman-thumbnailer.rb
107
+ - lib/middleman-thumbnailer/extension.rb
108
+ - lib/middleman-thumbnailer/thumbnail-generator.rb
109
+ - lib/middleman-thumbnailer/version.rb
110
+ - lib/middleman_extension.rb
111
+ - middleman-thumbnailer.gemspec
112
+ - spec/thumbnail-generator_spec.rb
113
+ homepage: https://github.com/nhemsley/middleman-thumbnailer
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.6.8
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Generate thumbnail versions of image files
137
+ test_files:
138
+ - features/namespace.feature
139
+ - features/step_definitions/server_steps.rb
140
+ - features/support/env.rb
141
+ - features/thumbnails.feature
142
+ - spec/thumbnail-generator_spec.rb