thingfish-processor-image 0.1.0.pre20161114102625

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 94cec81297dc83ba31fc6dfcf4b258688c4b232c
4
+ data.tar.gz: 5906271b9d85a5bc5f7da0a55f553d8e4ede3310
5
+ SHA512:
6
+ metadata.gz: 74ad63442c9b7cb228c955f172ded4091cf25a1267581d323f9ad8b9a526ce37eb546a81fb0c3d50c18ae03dfe4749dcc26ecfe85897734cdd7140da3f6ac6d5
7
+ data.tar.gz: 89fd3b8a06e6bce83d42238472dad1b503863f8676579e3d2561aa62c59b3e4937a889c5ebe79ae95180cc89598d0f249babb1b60e13e5219b2329962b7dcafc
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ README.md
3
+ ChangeLog.md
4
+
5
+ LICENSE.txt
data/.editorconfig ADDED
@@ -0,0 +1,14 @@
1
+ # http://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ # Unix-style newlines with a newline ending every file
7
+ [*]
8
+ end_of_line = lf
9
+ insert_final_newline = true
10
+
11
+ # Tab indentation
12
+ [**.*]
13
+ indent_style = tab
14
+
data/.rdoc_options ADDED
@@ -0,0 +1,16 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - .
6
+ charset: UTF-8
7
+ exclude:
8
+ hyperlink_all: false
9
+ line_numbers: false
10
+ main_page: README.md
11
+ markup: markdown
12
+ show_hash: false
13
+ tab_width: 8
14
+ title: Thingfish-Processor-Image Documentation
15
+ visibility: :protected
16
+ webcvs:
data/.simplecov ADDED
@@ -0,0 +1,9 @@
1
+ # Simplecov config
2
+
3
+ SimpleCov.start do
4
+ add_filter 'spec'
5
+ add_filter 'integration'
6
+ add_group "Needing tests" do |file|
7
+ file.covered_percent < 90
8
+ end
9
+ end
data/ChangeLog ADDED
@@ -0,0 +1,24 @@
1
+ 2016-11-14 Michael Granger <ged@FaerieMUD.org>
2
+
3
+ * History.md, README.md, Rakefile, lib/thingfish/processor/image.rb,
4
+ thingfish-processor-image.gemspec:
5
+ Fix up the README, Rakefile.
6
+ [e04ece44aa56] [tip]
7
+
8
+ 2016-11-13 Michael Granger <ged@FaerieMUD.org>
9
+
10
+ * thingfish-processor-image.gemspec:
11
+ Add the gemspec
12
+ [a43952fd8558] [github/master]
13
+
14
+ * .hgignore, Manifest.txt, Rakefile:
15
+ Update manifest, specify minimum version of Mongrel2
16
+ [8f7523aa9de2]
17
+
18
+ * .document, .editorconfig, .gems, .hgignore, .pryrc, .rdoc_options,
19
+ .ruby-gemset, .ruby-version, .simplecov, Gemfile, History.md,
20
+ LICENSE.txt, Manifest.txt, README.md, Rakefile, certs/ged.pem,
21
+ lib/thingfish/processor/image.rb, spec/data/skunks.jpg,
22
+ spec/spec_helper.rb, spec/thingfish/processor/image_spec.rb:
23
+ Exported from Angelfish
24
+ [3995d4e06f28]
data/History.md ADDED
@@ -0,0 +1,4 @@
1
+ ## v0.1.0 [YYYY-MM-DD] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ Initial release.
4
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Michael Granger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ .document
2
+ .editorconfig
3
+ .rdoc_options
4
+ .simplecov
5
+ ChangeLog
6
+ History.md
7
+ LICENSE.txt
8
+ Manifest.txt
9
+ README.md
10
+ Rakefile
11
+ lib/thingfish/processor/image.rb
12
+ spec/data/skunks.jpg
13
+ spec/spec_helper.rb
14
+ spec/thingfish/processor/image_spec.rb
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Thingfish-Processor-Image
2
+
3
+ home
4
+ : http://deveiate.org/projects/Thingfish-Processor-Image
5
+
6
+ code
7
+ : http://bitbucket.org/ged/Thingfish-Processor-Image
8
+
9
+ github
10
+ : https://github.com/ged/thingfish-processor-image
11
+
12
+ docs
13
+ : http://deveiate.org/code/thingfish-processor-image
14
+
15
+
16
+ ## Description
17
+
18
+ This is a basic image-processor plugin for the Thingfish digital asset manager.
19
+ It extracts image-related metadata from uploaded media files, and generates one
20
+ or more thumbnail images as related resources.
21
+
22
+
23
+ ## Authors
24
+
25
+ * Michael Granger <ged@FaerieMUD.org>
26
+
27
+
28
+ ## Installation
29
+
30
+ This plugin relies on [ImageMagick](http://www.imagemagick.org/), so you'll
31
+ need to have that installed as well as any `-dev` dependencies appropriate for
32
+ your system.
33
+
34
+ One you've done that:
35
+
36
+ $ gem install thingfish-processor-image
37
+
38
+
39
+ ## Usage
40
+
41
+ As with Thingfish itself, this plugin uses
42
+ Configurability[https://rubygems.org/gems/configurability] to modify default
43
+ behaviors.
44
+
45
+ Here's an example configuration file that enables this plugin.
46
+
47
+ --
48
+ thingfish:
49
+ processors:
50
+ - image
51
+
52
+ images:
53
+ thumbnail_dimensions: 150x150
54
+
55
+
56
+
57
+ ## Contributing
58
+
59
+ You can check out the current development source with Mercurial via its
60
+ {project page}[http://bitbucket.org/ged/thingfish-processor-image]. Or if you prefer Git, via
61
+ {its Github mirror}[https://github.com/ged/thingfish-processor-image].
62
+
63
+ After checking out the source, run:
64
+
65
+ $ rake newb
66
+
67
+ This task will install any missing dependencies, run the tests/specs,
68
+ and generate the API documentation.
69
+
70
+
71
+ ## License
72
+
73
+ Copyright (c) 2016, Michael Granger
74
+ All rights reserved.
75
+
76
+ Redistribution and use in source and binary forms, with or without
77
+ modification, are permitted provided that the following conditions are met:
78
+
79
+ * Redistributions of source code must retain the above copyright notice,
80
+ this list of conditions and the following disclaimer.
81
+
82
+ * Redistributions in binary form must reproduce the above copyright notice,
83
+ this list of conditions and the following disclaimer in the documentation
84
+ and/or other materials provided with the distribution.
85
+
86
+ * Neither the name of the author/s, nor the names of the project's
87
+ contributors may be used to endorse or promote products derived from this
88
+ software without specific prior written permission.
89
+
90
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
91
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
92
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
93
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
94
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
95
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
96
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
97
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
98
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
99
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
100
+
101
+
data/Rakefile ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env rake
2
+
3
+ begin
4
+ require 'hoe'
5
+ rescue LoadError
6
+ abort "This Rakefile requires hoe (gem install hoe)"
7
+ end
8
+
9
+ GEMSPEC = 'thingfish-processor-image.gemspec'
10
+
11
+
12
+ Hoe.plugin :mercurial
13
+ Hoe.plugin :signing
14
+ Hoe.plugin :deveiate
15
+
16
+ Hoe.plugins.delete :rubyforge
17
+ Hoe.plugins.delete :gemcutter # Remove for public gems
18
+
19
+ hoespec = Hoe.spec 'thingfish-processor-image' do |spec|
20
+ spec.readme_file = 'README.md'
21
+ spec.history_file = 'History.md'
22
+ spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
23
+ spec.urls = {
24
+ home: 'https://bitbucket.org/ged/thingfish-processor-image',
25
+ code: 'https://bitbucket.org/ged/thingfish-processor-image',
26
+ docs: 'https://deveiate.org/code/thingfish-processor-image',
27
+ github: 'https://github.com/ged/thingfish-processor-image',
28
+ }
29
+
30
+ spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
31
+ spec.license 'BSD-3-Clause'
32
+
33
+ spec.developer 'Michael Granger', 'ged@FaerieMUD.org'
34
+
35
+ spec.dependency 'mongrel2', '>= 0.46'
36
+ spec.dependency 'thingfish', '~> 0.5'
37
+ spec.dependency 'loggability', '~> 0.11'
38
+ spec.dependency 'rmagick', '~> 2.16'
39
+
40
+ spec.dependency 'hoe-deveiate', '~> 0.3', :developer
41
+ spec.dependency 'simplecov', '~> 0.7', :developer
42
+ spec.dependency 'rdoc-generator-fivefish', '~> 0.1', :developer
43
+
44
+ spec.require_ruby_version( '>=2.3.1' )
45
+ spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
46
+ spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
47
+
48
+ self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
49
+ end
50
+
51
+
52
+ ENV['VERSION'] ||= hoespec.spec.version.to_s
53
+
54
+ # Run the tests before checking in
55
+ task 'hg:precheckin' => [ :check_history, :check_manifest, :gemspec, :spec ]
56
+
57
+ task :test => :spec
58
+
59
+ # Rebuild the ChangeLog immediately before release
60
+ task :prerelease => 'ChangeLog'
61
+ CLOBBER.include( 'ChangeLog' )
62
+
63
+ desc "Build a coverage report"
64
+ task :coverage do
65
+ ENV["COVERAGE"] = 'yes'
66
+ Rake::Task[:spec].invoke
67
+ end
68
+ CLOBBER.include( 'coverage' )
69
+
70
+
71
+ # Use the fivefish formatter for docs generated from development checkout
72
+ if File.directory?( '.hg' )
73
+ require 'rdoc/task'
74
+
75
+ Rake::Task[ 'docs' ].clear
76
+ RDoc::Task.new( 'docs' ) do |rdoc|
77
+ rdoc.main = "README.rdoc"
78
+ rdoc.markup = 'markdown'
79
+ rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb" )
80
+ rdoc.generator = :fivefish
81
+ rdoc.title = 'Thingfish-Processor-Image'
82
+ rdoc.rdoc_dir = 'doc'
83
+ end
84
+ end
85
+
86
+ task :gemspec => GEMSPEC
87
+ file GEMSPEC => __FILE__
88
+ task GEMSPEC do |task|
89
+ spec = $hoespec.spec
90
+ spec.files.delete( '.gemtest' )
91
+ spec.signing_key = nil
92
+ spec.cert_chain = Dir.glob( 'certs/*.pem' )
93
+ spec.version = "#{spec.version.bump}.0.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
94
+ File.open( task.name, 'w' ) do |fh|
95
+ fh.write( spec.to_ruby )
96
+ end
97
+ end
98
+ CLOBBER.include( GEMSPEC.to_s )
99
+
100
+ task :default => :gemspec
101
+
@@ -0,0 +1,322 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'rmagick'
5
+
6
+ require 'strelka'
7
+ require 'strelka/httprequest/acceptparams'
8
+
9
+ require 'thingfish'
10
+ require 'thingfish/processor'
11
+
12
+
13
+ # Image processor plugin for Thingfish
14
+ class Thingfish::Processor::Image < Thingfish::Processor
15
+ extend Loggability,
16
+ Configurability,
17
+ Strelka::MethodUtilities
18
+
19
+
20
+ # Package version
21
+ VERSION = '0.1.0'
22
+
23
+ # Version control revision
24
+ REVISION = %q$Revision: e04ece44aa56 $
25
+
26
+
27
+ # Loggability API -- log to the :angelfish logger
28
+ log_to :thingfish
29
+
30
+ # Configurability API -- use the 'image_processor' section of the config
31
+ config_key :image_processor
32
+
33
+
34
+ # The default dimensions of thumbnails
35
+ CONFIG_DEFAULTS = {
36
+ thumbnail_dimensions: '100x100',
37
+ }
38
+
39
+ # An array of mediatypes to ignore, even though ImageMagick claims it groks them
40
+ IGNORED_MIMETYPES = %w[
41
+ application/octet-stream
42
+ text/html
43
+ text/plain
44
+ text/x-server-parsed-html
45
+ ]
46
+
47
+
48
+ ##
49
+ # The (maximum) dimensions of generated thumbnails
50
+ singleton_attr_accessor :thumbnail_dimensions
51
+ @thumbnail_dimensions = [ 100, 100 ]
52
+
53
+
54
+ ### Configurability API -- configure the processor with the :images section of
55
+ ### the config.
56
+ def self::configure( config=nil )
57
+ config = self.defaults.merge( config || {} )
58
+
59
+ self.thumbnail_dimensions = config[:thumbnail_dimensions].split('x', 2).map( &:to_i )
60
+ end
61
+
62
+
63
+
64
+ #################################################################
65
+ ### I N S T A N C E M E T H O D S
66
+ #################################################################
67
+
68
+ ### Set up a new Filter object
69
+ def initialize( * ) # :notnew:
70
+ super
71
+
72
+ @supported_formats = find_supported_formats()
73
+ @handled_types = find_handled_types( @supported_formats )
74
+ @generated_types = find_generated_types( @supported_formats )
75
+ end
76
+
77
+
78
+ ######
79
+ public
80
+ ######
81
+
82
+ # The Hash of formats and the operations they support.
83
+ attr_reader :supported_formats
84
+
85
+ # The mediatypes the plugin accepts in a request, as ThingFish::AcceptParam
86
+ # objects
87
+ attr_reader :handled_types
88
+
89
+ # The mediatypes the plugin can generated in a response, as
90
+ # ThingFish::AcceptParam objects
91
+ attr_reader :generated_types
92
+
93
+
94
+ ### Returns +true+ if the given media +type+ is one the processor handles. Overridden so
95
+ ### the types can be used by the instance.
96
+ def handled_type?( type )
97
+ self.log.debug "Looking for handled type for: %p" % [ type ]
98
+ result = self.handled_types.find {|handled_type| type =~ handled_type }
99
+ self.log.debug " found: %p" % [ result ]
100
+ return result
101
+ end
102
+ alias_method :is_handled_type?, :handled_type?
103
+
104
+
105
+ ### Synchronous processor API -- extract metadata from uploaded images.
106
+ def on_request( request )
107
+ self.log.debug "Image-processing %p" % [ request ]
108
+ image = case request.body
109
+ when StringIO
110
+ self.log.debug " making a single image from a StringIO"
111
+ Magick::Image.from_blob( request.body.read )
112
+ else
113
+ self.log.debug " making a flattened image out of %p" % [ request.body.path ]
114
+ list = Magick::ImageList.new( request.body.path )
115
+ list.flatten_images
116
+ end
117
+
118
+ image = image.first if image.respond_to?( :first )
119
+ self.log.debug " image is: %p" % [ image ]
120
+ metadata = self.extract_image_metadata( image )
121
+ self.log.debug " extracted image metadata: %p" % [ metadata ]
122
+ request.add_metadata( metadata )
123
+
124
+ self.log.debug " going to generate a thumbnail..."
125
+ self.generate_thumbnail( image, metadata['title'] ) do |thumbio, thumb_metadata|
126
+ self.log.debug " generated: %p (%p)" % [ thumbio, thumb_metadata ]
127
+ request.add_related_resource( thumbio, thumb_metadata )
128
+ end
129
+
130
+ self.log.debug " destroying the image to free up memory"
131
+ image.destroy!
132
+ end
133
+
134
+
135
+ ### Return a human-readable representation of the receiving object suitable for
136
+ ### debugging.
137
+ def inspect
138
+ return "#<%p:%#x %d supported image formats, %d readable, %d writable>" % [
139
+ self.class,
140
+ self.object_id * 2,
141
+ self.supported_formats.size,
142
+ self.handled_types.size,
143
+ self.generated_types.size,
144
+ ]
145
+ end
146
+
147
+
148
+ #########
149
+ protected
150
+ #########
151
+
152
+ ### Extract metadata from the given +image+ (a Magick::Image object) and return it in
153
+ ### a Hash.
154
+ def extract_image_metadata( image )
155
+ metadata = {}
156
+
157
+ metadata.merge!( self.get_regular_metadata(image) )
158
+ metadata.merge!( self.get_exif_metadata(image) )
159
+
160
+ return metadata
161
+ end
162
+
163
+
164
+ ### Fetch regular image metadata as a Hash.
165
+ def get_regular_metadata( image )
166
+ return {
167
+ 'image:height' => image.rows,
168
+ 'image:width' => image.columns,
169
+ 'image:depth' => image.depth,
170
+ 'image:density' => image.density,
171
+ 'image:gamma' => image.gamma,
172
+ 'image:bounding_box' => image.bounding_box.to_s,
173
+ }
174
+ end
175
+
176
+
177
+ ### Fetch exif metadata and return it as a Hash.
178
+ def get_exif_metadata( image )
179
+ exif_pairs = image.get_exif_by_entry
180
+ exif_pairs.reject! {|name, val| name == 'unknown' || val.nil? }
181
+ exif_pairs.collect! do |name, val|
182
+ newname = name.gsub(/\B([A-Z])(?=[a-z])/) { '_' + $1 }.downcase
183
+ [ "exif:#{newname}", val ]
184
+ end
185
+ return Hash[ exif_pairs ]
186
+ end
187
+
188
+
189
+ ### Create a thumbnail from the given +image+ and return it in a string along with any
190
+ ### associated metadata.
191
+ def generate_thumbnail( image, title )
192
+ dimensions = self.class.thumbnail_dimensions
193
+ self.log.debug "Making thumbnail of max dimensions: [%d X %d]" % dimensions
194
+ thumb = image.resize_to_fit( *dimensions )
195
+ imgdata = StringIO.new
196
+ imgdata << thumb.to_blob {|img| img.format = 'JPG' }
197
+ imgdata.rewind
198
+
199
+ metadata = self.extract_image_metadata( thumb )
200
+ metadata.merge!({
201
+ format: thumb.mime_type,
202
+ relationship: 'thumbnail',
203
+ title: "Thumbnail of %s" % [ title || image.inspect ],
204
+ extent: imgdata.size,
205
+ })
206
+
207
+ self.log.debug " made thumbnail for %p" % [ image ]
208
+ yield( imgdata, metadata )
209
+
210
+ thumb.destroy!
211
+ end
212
+
213
+
214
+ # A struct that can represent the support in the installed ImageMagick for
215
+ # common operations. See Magick.formats for details.
216
+ class MagickOperations
217
+
218
+ ### Create a new MagickOperations for the given +ext+, reading the
219
+ ### supported features of the format from +support_string+.
220
+ def initialize( ext, support_string )
221
+ @ext = ext
222
+ @blob, @read, @write, @multi = support_string.split('')
223
+ end
224
+
225
+ ##
226
+ # Supported features
227
+ attr_reader :ext, :blob, :read, :write, :multi
228
+
229
+
230
+ ### Return a human-readable description of the operations spec
231
+ def to_s
232
+ return [
233
+ self.has_native_blob? ? "Blob" : nil,
234
+ self.can_read? ? "Readable" : nil,
235
+ self.can_write? ? "Writable" : nil,
236
+ self.supports_multi? ? "Multi" : nil,
237
+ ].compact.join(',')
238
+ end
239
+
240
+
241
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
242
+ ### support for the associated type
243
+ def has_native_blob?
244
+ return (@blob == '*')
245
+ end
246
+
247
+
248
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
249
+ ### support for the associated type
250
+ def can_read?
251
+ return (@read == 'r')
252
+ end
253
+
254
+
255
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
256
+ ### support for the associated type
257
+ def can_write?
258
+ return (@write == 'w')
259
+ end
260
+
261
+
262
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
263
+ ### support for the associated type
264
+ def supports_multi?
265
+ return (@multi == '+')
266
+ end
267
+
268
+ end # class MagickOperations
269
+
270
+
271
+ ### Transform the installed ImageMagick's list of formats into AcceptParams
272
+ ### for easy comparison later.
273
+ def find_supported_formats
274
+ formats = {}
275
+ raise "Config database doesn't have any mimetypes" if
276
+ Mongrel2::Config.mimetypes.empty?
277
+
278
+ # A hash of image formats and their properties. Each key in the returned
279
+ # hash is the name of a supported image format. Each value is a string in
280
+ # the form "BRWA". The details are in this table:
281
+ # B is "*" if the format has native blob support, and "-" otherwise.
282
+ # R is "r" if ×Magick can read the format, and "-" otherwise.
283
+ # W is "w" if ×Magick can write the format, and "-" otherwise.
284
+ # A is "+" if the format supports multi-image files, and "-" otherwise.
285
+ Magick.formats.each do |ext,support|
286
+ ext = ".#{ext.downcase}"
287
+ self.log.debug "Looking for mediatype for ext: %s (%p)" % [ ext, support ]
288
+
289
+ mimetype = Mongrel2::Config.mimetypes[ ext ] or next
290
+ self.log.debug " mimetype is: %p" % [ mimetype ]
291
+ next if IGNORED_MIMETYPES.include?( mimetype )
292
+
293
+ operations = MagickOperations.new( ext, support )
294
+ self.log.debug " registering image format %s (%s)" % [ mimetype, operations ]
295
+ formats[ mimetype ] = operations
296
+ end
297
+
298
+ self.log.debug "Registered mimetype mapping for %d of %d supported image types" %
299
+ [ formats.keys.length, Magick.formats.length ]
300
+ return formats
301
+ end
302
+
303
+
304
+ ### Return Strelka::HTTPRequest::MediaType objects for the formats that the
305
+ ### processor is capable of reading.
306
+ def find_handled_types( supported_formats )
307
+ return supported_formats.
308
+ select {|type, op| op.can_read? }.
309
+ collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
310
+ end
311
+
312
+
313
+ ### Return an Array of Strelka::HTTPRequest::MediaType objects for the
314
+ ### formats that the processor is capable of writing.
315
+ def find_generated_types( supported_formats )
316
+ return supported_formats.
317
+ select {|type, op| op.can_write? }.
318
+ collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
319
+ end
320
+
321
+ end # class ThingFish::Processor::Image
322
+
Binary file
@@ -0,0 +1,50 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'simplecov' if ENV['COVERAGE']
5
+
6
+ require 'rspec'
7
+
8
+ require 'mongrel2'
9
+ require 'mongrel2/testing'
10
+
11
+ require 'thingfish'
12
+ require 'thingfish/spechelpers'
13
+
14
+ require 'loggability/spechelpers'
15
+
16
+ require 'thingfish/processor/image'
17
+
18
+
19
+ module Thingfish::Processor::Image::SpecHelpers
20
+
21
+ FIXTURE_DIR = Pathname( __FILE__ ).dirname + 'data'
22
+
23
+
24
+ ### Load and return the data from the fixture with the specified +filename+.
25
+ def fixture_data( filename )
26
+ fixture = FIXTURE_DIR + filename
27
+ return fixture.open( 'r', encoding: 'binary' )
28
+ end
29
+
30
+ end
31
+
32
+
33
+
34
+ ### Mock with RSpec
35
+ RSpec.configure do |config|
36
+ config.run_all_when_everything_filtered = true
37
+ config.filter_run :focus
38
+ config.order = 'random'
39
+ config.mock_with( :rspec ) do |mock|
40
+ mock.syntax = :expect
41
+ end
42
+
43
+ config.include( Mongrel2::SpecHelpers )
44
+ config.include( Thingfish::SpecHelpers )
45
+ config.include( Loggability::SpecHelpers )
46
+ config.include( Thingfish::Processor::Image::SpecHelpers )
47
+ end
48
+
49
+
50
+
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../../spec_helper'
4
+
5
+ require 'rspec'
6
+ require 'strelka'
7
+ require 'strelka/httprequest/metadata'
8
+ require 'thingfish/processor/image'
9
+
10
+
11
+
12
+ describe Thingfish::Processor::Image, :db do
13
+
14
+ before( :all ) do
15
+ Strelka::HTTPRequest.class_eval { include Strelka::HTTPRequest::Metadata }
16
+ end
17
+
18
+
19
+ let( :processor ) { Thingfish::Processor.create(:image) }
20
+ let( :factory ) do
21
+ Mongrel2::RequestFactory.new(
22
+ :route => '/',
23
+ :headers => {:accept => '*/*'})
24
+ end
25
+
26
+
27
+ it "extracts image metadata from uploaded images" do
28
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
29
+
30
+ processor.process_request( req )
31
+
32
+ expect( req.metadata ).to include( "image:bounding_box" => "width=640, height=480, x=0, y=0" )
33
+ end
34
+
35
+
36
+ it "extracts exif metadata from uploaded images" do
37
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
38
+
39
+ processor.process_request( req )
40
+
41
+ expect( req.metadata ).to include( "exif:software" => "Pixelmator 3.1" )
42
+ end
43
+
44
+
45
+ it "creates a thumbnail of uploaded images" do
46
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
47
+
48
+ processor.process_request( req )
49
+
50
+ expect( req.related_resources.size ).to eq( 1 )
51
+ expect( req.related_resources.keys.first ).to be_a( StringIO )
52
+ expect( req.related_resources.values.first ).
53
+ to include( 'image:height' => 75, 'image:width' => 100 )
54
+ end
55
+
56
+
57
+ it "extracts IPTC metadata from uploaded images"
58
+
59
+ end
60
+
61
+ # vim: set nosta noet ts=4 sw=4 ft=rspec:
metadata ADDED
@@ -0,0 +1,248 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thingfish-processor-image
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre20161114102625
5
+ platform: ruby
6
+ authors:
7
+ - Michael Granger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEbDCCAtSgAwIBAgIBATANBgkqhkiG9w0BAQsFADA+MQwwCgYDVQQDDANnZWQx
14
+ GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
15
+ HhcNMTYwODIwMTgxNzQyWhcNMTcwODIwMTgxNzQyWjA+MQwwCgYDVQQDDANnZWQx
16
+ GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
17
+ ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC/JWGRHO+USzR97vXjkFgt
18
+ 83qeNf2KHkcvrRTSnR64i6um/ziin0I0oX23H7VYrDJC9A/uoUa5nGRJS5Zw/+wW
19
+ ENcvWVZS4iUzi4dsYJGY6yEOsXh2CcF46+QevV8iE+UmbkU75V7Dy1JCaUOyizEt
20
+ TH5UHsOtUU7k9TYARt/TgYZKuaoAMZZd5qyVqhF1vV+7/Qzmp89NGflXf2xYP26a
21
+ 4MAX2qqKX/FKXqmFO+AGsbwYTEds1mksBF3fGsFgsQWxftG8GfZQ9+Cyu2+l1eOw
22
+ cZ+lPcg834G9DrqW2zhqUoLr1MTly4pqxYGb7XoDhoR7dd1kFE2a067+DzWC/ADt
23
+ +QkcqWUm5oh1fN0eqr7NsZlVJDulFgdiiYPQiIN7UNsii4Wc9aZqBoGcYfBeQNPZ
24
+ soo/6za/bWajOKUmDhpqvaiRv9EDpVLzuj53uDoukMMwxCMfgb04+ckQ0t2G7wqc
25
+ /D+K9JW9DDs3Yjgv9k4h7YMhW5gftosd+NkNC/+Y2CkCAwEAAaN1MHMwCQYDVR0T
26
+ BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHKN/nkRusdqCJEuq3lgB3fJvyTg
27
+ MBwGA1UdEQQVMBOBEWdlZEBGYWVyaWVNVUQub3JnMBwGA1UdEgQVMBOBEWdlZEBG
28
+ YWVyaWVNVUQub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAPJzKiT0zBU7kpqe0aS2qb
29
+ FI0PJ4y5I8buU4IZGUD5NEt/N7pZNfOyBxkrZkXhS44Fp+xwBH5ebLbq/WY78Bqd
30
+ db0z6ZgW4LMYMpWFfbXsRbd9TU2f52L8oMAhxOvF7Of5qJMVWuFQ8FPagk2iHrdH
31
+ inYLQagqAF6goWTXgAJCdPd6SNeeSNqA6vlY7CV1Jh5kfNJJ6xu/CVij1GzCLu/5
32
+ DMOr26DBv+qLJRRC/2h34uX71q5QgeOyxvMg+7V3u/Q06DXyQ2VgeeqiwDFFpEH0
33
+ PFkdPO6ZqbTRcLfNH7mFgCBJjsfSjJrn0sPBlYyOXgCoByfZnZyrIMH/UY+lgQqS
34
+ 6Von1VDsfQm0eJh5zYZD64ZF86phSR7mUX3mXItwH04HrZwkWpvgd871DZVR3i1n
35
+ w8aNA5re5+Rt/Vvjxj5AcEnZnZiz5x959NaddQocX32Z1unHw44pzRNUur1GInfW
36
+ p4vpx2kUSFSAGjtCbDGTNV2AH8w9OU4xEmNz8c5lyoA=
37
+ -----END CERTIFICATE-----
38
+ date: 2016-11-14 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: mongrel2
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.46'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0.46'
54
+ - !ruby/object:Gem::Dependency
55
+ name: thingfish
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.5'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.5'
68
+ - !ruby/object:Gem::Dependency
69
+ name: loggability
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.11'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.11'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rmagick
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.16'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.16'
96
+ - !ruby/object:Gem::Dependency
97
+ name: hoe-mercurial
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.4'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.4'
110
+ - !ruby/object:Gem::Dependency
111
+ name: hoe-deveiate
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.8'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.8'
124
+ - !ruby/object:Gem::Dependency
125
+ name: hoe-highline
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.2'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.2'
138
+ - !ruby/object:Gem::Dependency
139
+ name: simplecov
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.7'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.7'
152
+ - !ruby/object:Gem::Dependency
153
+ name: rdoc-generator-fivefish
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.1'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.1'
166
+ - !ruby/object:Gem::Dependency
167
+ name: rdoc
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '4.0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '4.0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: hoe
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.15'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.15'
194
+ description: |-
195
+ This is a basic image-processor plugin for the Thingfish digital asset manager.
196
+ It extracts image-related metadata from uploaded media files, and generates one
197
+ or more thumbnail images as related resources.
198
+ email:
199
+ - ged@FaerieMUD.org
200
+ executables: []
201
+ extensions: []
202
+ extra_rdoc_files:
203
+ - History.md
204
+ - LICENSE.txt
205
+ - Manifest.txt
206
+ - README.md
207
+ files:
208
+ - ".document"
209
+ - ".editorconfig"
210
+ - ".rdoc_options"
211
+ - ".simplecov"
212
+ - ChangeLog
213
+ - History.md
214
+ - LICENSE.txt
215
+ - Manifest.txt
216
+ - README.md
217
+ - Rakefile
218
+ - lib/thingfish/processor/image.rb
219
+ - spec/data/skunks.jpg
220
+ - spec/spec_helper.rb
221
+ - spec/thingfish/processor/image_spec.rb
222
+ homepage: https://bitbucket.org/ged/thingfish-processor-image
223
+ licenses:
224
+ - BSD-3-Clause
225
+ metadata: {}
226
+ post_install_message:
227
+ rdoc_options:
228
+ - "--main"
229
+ - README.md
230
+ require_paths:
231
+ - lib
232
+ required_ruby_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: 2.3.1
237
+ required_rubygems_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">"
240
+ - !ruby/object:Gem::Version
241
+ version: 1.3.1
242
+ requirements: []
243
+ rubyforge_project:
244
+ rubygems_version: 2.5.1
245
+ signing_key:
246
+ specification_version: 4
247
+ summary: This is a basic image-processor plugin for the Thingfish digital asset manager
248
+ test_files: []