thingfish-processor-image 0.1.0.pre20161114102625
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.editorconfig +14 -0
- data/.rdoc_options +16 -0
- data/.simplecov +9 -0
- data/ChangeLog +24 -0
- data/History.md +4 -0
- data/LICENSE.txt +20 -0
- data/Manifest.txt +14 -0
- data/README.md +101 -0
- data/Rakefile +101 -0
- data/lib/thingfish/processor/image.rb +322 -0
- data/spec/data/skunks.jpg +0 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/thingfish/processor/image_spec.rb +61 -0
- metadata +248 -0
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
data/.editorconfig
ADDED
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
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
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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|