middleman-thumbnailer 0.1.2

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.
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