dynamic_image 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +5 -5
- data/lib/dynamic_image/belongs_to.rb +4 -4
- data/lib/dynamic_image/controller.rb +26 -25
- data/lib/dynamic_image/digest_verifier.rb +9 -3
- data/lib/dynamic_image/errors.rb +2 -1
- data/lib/dynamic_image/helper.rb +29 -32
- data/lib/dynamic_image/image_reader.rb +48 -0
- data/lib/dynamic_image/image_sizing.rb +6 -8
- data/lib/dynamic_image/metadata.rb +26 -22
- data/lib/dynamic_image/model/dimensions.rb +3 -8
- data/lib/dynamic_image/model/validations.rb +21 -23
- data/lib/dynamic_image/model.rb +8 -9
- data/lib/dynamic_image/processed_image.rb +11 -16
- data/lib/dynamic_image/railtie.rb +1 -1
- data/lib/dynamic_image/routing.rb +1 -1
- data/lib/dynamic_image/version.rb +1 -1
- data/lib/dynamic_image.rb +1 -0
- data/lib/rails/generators/dynamic_image/resource/resource_generator.rb +16 -21
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3daa2b118705fafabe134cb7f5707d4d714d67a
|
4
|
+
data.tar.gz: 46ea0855ee40eb1ab22787daab087369eaae5c11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1b7ce51abac870c79574a04cc4d57dc016118590433588757748c508693d2fd7bae9e33c7d1db70d8d6be64b745ec2d364b6c8f0c42373634278bedc6e2b612
|
7
|
+
data.tar.gz: 16b8a043dec55f1f430860217612e6a1ba15dad53ad91e89d41611764c1265a5067f512a5d5969b8fd725a9a20b716e369950ce1f0a6e371b19ade29996d698c
|
data/Rakefile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
3
|
|
4
|
-
APP_RAKEFILE =
|
4
|
+
APP_RAKEFILE = 'spec/internal/Rakefile'.freeze
|
5
5
|
load 'rails/tasks/engine.rake'
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new
|
8
8
|
|
9
|
-
task :
|
10
|
-
task :
|
9
|
+
task default: :spec
|
10
|
+
task test: :spec
|
@@ -7,16 +7,16 @@ module DynamicImage
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
-
def belongs_to_image(name, scope=nil, options={})
|
10
|
+
def belongs_to_image(name, scope = nil, options = {})
|
11
11
|
belongs_to(name, scope, options)
|
12
12
|
|
13
13
|
define_method "#{name}=" do |new_image|
|
14
|
-
if new_image.present? && !new_image.
|
15
|
-
new_image =
|
14
|
+
if new_image.present? && !new_image.is_a?(DynamicImage::Model)
|
15
|
+
new_image = send("build_#{name}", file: new_image)
|
16
16
|
end
|
17
17
|
super(new_image)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
@@ -29,15 +29,14 @@ module DynamicImage
|
|
29
29
|
|
30
30
|
# Renders the original image data, without any processing.
|
31
31
|
def original
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
32
|
+
return unless stale?(@record)
|
33
|
+
respond_to do |format|
|
34
|
+
format.any(:gif, :jpeg, :png, :tiff) do
|
35
|
+
send_data(
|
36
|
+
@record.data,
|
37
|
+
content_type: @record.content_type,
|
38
|
+
disposition: 'inline'
|
39
|
+
)
|
41
40
|
end
|
42
41
|
end
|
43
42
|
end
|
@@ -58,20 +57,14 @@ module DynamicImage
|
|
58
57
|
end
|
59
58
|
|
60
59
|
def render_image(options)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
send_data(
|
70
|
-
processed_image.cropped_and_resized(requested_size),
|
71
|
-
content_type: processed_image.content_type,
|
72
|
-
disposition: 'inline'
|
73
|
-
)
|
74
|
-
end
|
60
|
+
return unless stale?(@record)
|
61
|
+
respond_to do |format|
|
62
|
+
format.html do
|
63
|
+
render(file: File.join(File.dirname(__FILE__), 'templates/show'),
|
64
|
+
layout: false, locals: { options: options })
|
65
|
+
end
|
66
|
+
format.any(:gif, :jpeg, :png, :tiff) do
|
67
|
+
send_image(DynamicImage::ProcessedImage.new(@record, options))
|
75
68
|
end
|
76
69
|
end
|
77
70
|
end
|
@@ -80,10 +73,18 @@ module DynamicImage
|
|
80
73
|
params[:format]
|
81
74
|
end
|
82
75
|
|
76
|
+
def send_image(image)
|
77
|
+
send_data(
|
78
|
+
image.cropped_and_resized(requested_size),
|
79
|
+
content_type: image.content_type,
|
80
|
+
disposition: 'inline'
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
83
84
|
def verify_signed_params
|
84
|
-
key = [:action, :id, :size].map
|
85
|
+
key = [:action, :id, :size].map do |k|
|
85
86
|
k == :id ? params.require(k).to_i : params.require(k)
|
86
|
-
|
87
|
+
end.join('-')
|
87
88
|
DynamicImage.digest_verifier.verify(key, params[:digest])
|
88
89
|
end
|
89
90
|
end
|
@@ -52,11 +52,17 @@ module DynamicImage
|
|
52
52
|
|
53
53
|
def generate_digest(data)
|
54
54
|
require 'openssl' unless defined?(OpenSSL)
|
55
|
-
OpenSSL::HMAC.hexdigest(
|
55
|
+
OpenSSL::HMAC.hexdigest(
|
56
|
+
OpenSSL::Digest.const_get(@digest).new,
|
57
|
+
@secret,
|
58
|
+
data
|
59
|
+
)
|
56
60
|
end
|
57
61
|
|
58
62
|
def valid_digest?(data, digest)
|
59
|
-
data.present? &&
|
63
|
+
data.present? &&
|
64
|
+
digest.present? &&
|
65
|
+
secure_compare(digest, generate_digest(data))
|
60
66
|
end
|
61
67
|
end
|
62
|
-
end
|
68
|
+
end
|
data/lib/dynamic_image/errors.rb
CHANGED
@@ -4,7 +4,8 @@ module DynamicImage
|
|
4
4
|
module Errors
|
5
5
|
class Error < StandardError; end
|
6
6
|
class InvalidImage < DynamicImage::Errors::Error; end
|
7
|
+
class InvalidHeader < DynamicImage::Errors::Error; end
|
7
8
|
class InvalidSignature < DynamicImage::Errors::Error; end
|
8
9
|
class InvalidSizeOptions < DynamicImage::Errors::Error; end
|
9
10
|
end
|
10
|
-
end
|
11
|
+
end
|
data/lib/dynamic_image/helper.rb
CHANGED
@@ -7,7 +7,7 @@ module DynamicImage
|
|
7
7
|
module Helper
|
8
8
|
# Returns the path for a DynamicImage::Model record.
|
9
9
|
# Takes the same options as +dynamic_image_url+
|
10
|
-
def dynamic_image_path(record_or_array, options={})
|
10
|
+
def dynamic_image_path(record_or_array, options = {})
|
11
11
|
dynamic_image_url(record_or_array, { routing_type: :path }.merge(options))
|
12
12
|
end
|
13
13
|
|
@@ -29,24 +29,18 @@ module DynamicImage
|
|
29
29
|
# # => <img alt="My file" height="200" src="..." width="320" />
|
30
30
|
# dynamic_image_tag(image, size: "100x100", alt="Avatar")
|
31
31
|
# # => <img alt="Avatar" height="62" src="..." width="100" />
|
32
|
-
def dynamic_image_tag(record_or_array, options={})
|
32
|
+
def dynamic_image_tag(record_or_array, options = {})
|
33
33
|
record = extract_dynamic_image_record(record_or_array)
|
34
|
-
options = {
|
35
|
-
alt: image_alt(record.filename)
|
36
|
-
}.merge(options)
|
34
|
+
options = { alt: image_alt(record.filename) }.merge(options)
|
37
35
|
|
38
36
|
size = fit_size!(record_or_array, options)
|
39
37
|
url_options = options.extract!(*allowed_dynamic_image_url_options)
|
40
38
|
html_options = { size: size }.merge(options)
|
41
39
|
|
42
|
-
image_tag(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
url_options
|
47
|
-
),
|
48
|
-
html_options
|
49
|
-
)
|
40
|
+
image_tag(dynamic_image_path_with_size(record_or_array,
|
41
|
+
size,
|
42
|
+
url_options),
|
43
|
+
html_options)
|
50
44
|
end
|
51
45
|
|
52
46
|
# Returns the URL for a DynamicImage::Model record.
|
@@ -71,38 +65,38 @@ module DynamicImage
|
|
71
65
|
# # => "http://example.com/images/72...c2/100x62/1-2014062020...00.jpg"
|
72
66
|
# dynamic_image_url(image, size: '100x100', crop: true)
|
73
67
|
# # => "http://example.com/images/a4...6b/100x100/1-2014062020...00.jpg"
|
74
|
-
def dynamic_image_url(record_or_array, options={})
|
68
|
+
def dynamic_image_url(record_or_array, options = {})
|
75
69
|
size = fit_size!(record_or_array, options)
|
76
70
|
dynamic_image_url_with_size(record_or_array, size, options)
|
77
71
|
end
|
78
72
|
|
79
73
|
# Returns a path to the original uploaded file, without any processing
|
80
74
|
# applied. Sizing options are not supported.
|
81
|
-
def original_dynamic_image_path(record_or_array, options={})
|
75
|
+
def original_dynamic_image_path(record_or_array, options = {})
|
82
76
|
dynamic_image_path(record_or_array, { action: :original }.merge(options))
|
83
77
|
end
|
84
78
|
|
85
79
|
# Returns a URL to the original uploaded file, without any processing
|
86
80
|
# applied. Sizing options are not supported.
|
87
|
-
def original_dynamic_image_url(record_or_array, options={})
|
81
|
+
def original_dynamic_image_url(record_or_array, options = {})
|
88
82
|
dynamic_image_url(record_or_array, { action: :original }.merge(options))
|
89
83
|
end
|
90
84
|
|
91
85
|
# Same as +dynamic_image_path+, but points to an image with any
|
92
86
|
# pre-cropping disabled.
|
93
|
-
def uncropped_dynamic_image_path(record_or_array, options={})
|
87
|
+
def uncropped_dynamic_image_path(record_or_array, options = {})
|
94
88
|
dynamic_image_path(record_or_array, { action: :uncropped }.merge(options))
|
95
89
|
end
|
96
90
|
|
97
91
|
# Same as +dynamic_image_tag+, but renders an image with any
|
98
92
|
# pre-cropping disabled.
|
99
|
-
def uncropped_dynamic_image_tag(record_or_array, options={})
|
93
|
+
def uncropped_dynamic_image_tag(record_or_array, options = {})
|
100
94
|
dynamic_image_tag(record_or_array, { action: :uncropped }.merge(options))
|
101
95
|
end
|
102
96
|
|
103
97
|
# Same as +dynamic_image_url+, but points to an image with any
|
104
98
|
# pre-cropping disabled.
|
105
|
-
def uncropped_dynamic_image_url(record_or_array, options={})
|
99
|
+
def uncropped_dynamic_image_url(record_or_array, options = {})
|
106
100
|
dynamic_image_url(record_or_array, { action: :uncropped }.merge(options))
|
107
101
|
end
|
108
102
|
|
@@ -120,16 +114,18 @@ module DynamicImage
|
|
120
114
|
Mime::Type.lookup(record.safe_content_type).to_sym
|
121
115
|
end
|
122
116
|
|
123
|
-
def dynamic_image_digest(record, action, size=nil)
|
117
|
+
def dynamic_image_digest(record, action, size = nil)
|
124
118
|
key = [action || 'show', record.id, size].compact.join('-')
|
125
119
|
DynamicImage.digest_verifier.generate(key)
|
126
120
|
end
|
127
121
|
|
128
|
-
def dynamic_image_path_with_size(record_or_array, size=nil, options={})
|
129
|
-
dynamic_image_url_with_size(record_or_array,
|
122
|
+
def dynamic_image_path_with_size(record_or_array, size = nil, options = {})
|
123
|
+
dynamic_image_url_with_size(record_or_array,
|
124
|
+
size,
|
125
|
+
{ routing_type: :path }.merge(options))
|
130
126
|
end
|
131
127
|
|
132
|
-
def dynamic_image_url_with_size(record_or_array, size=nil, options={})
|
128
|
+
def dynamic_image_url_with_size(record_or_array, size = nil, options = {})
|
133
129
|
record = extract_dynamic_image_record(record_or_array)
|
134
130
|
options = {
|
135
131
|
routing_type: :url,
|
@@ -137,7 +133,8 @@ module DynamicImage
|
|
137
133
|
format: default_format_for_image(record),
|
138
134
|
size: size
|
139
135
|
}.merge(options)
|
140
|
-
options[:digest] =
|
136
|
+
options[:digest] =
|
137
|
+
dynamic_image_digest(record, options[:action], options[:size])
|
141
138
|
polymorphic_url(record_or_array, options)
|
142
139
|
end
|
143
140
|
|
@@ -154,17 +151,17 @@ module DynamicImage
|
|
154
151
|
record = extract_dynamic_image_record(record_or_array)
|
155
152
|
action = options[:action].try(:to_s)
|
156
153
|
size_opts = options.extract!(:size, :crop, :upscale)
|
157
|
-
|
158
154
|
if size_opts[:size]
|
159
|
-
|
160
|
-
record,
|
161
|
-
uncropped: (action == "uncropped")
|
162
|
-
).fit(size_opts[:size], size_opts).floor.to_s
|
163
|
-
elsif action == "original"
|
164
|
-
record.real_size.floor.to_s
|
155
|
+
image_sizing(record, size_opts, (action == 'uncropped'))
|
165
156
|
else
|
166
|
-
record.size.floor.to_s
|
157
|
+
(action == 'original' ? record.real_size : record.size).floor.to_s
|
167
158
|
end
|
168
159
|
end
|
160
|
+
|
161
|
+
def image_sizing(record, size_opts, uncropped)
|
162
|
+
ImageSizing
|
163
|
+
.new(record, uncropped: uncropped)
|
164
|
+
.fit(size_opts[:size], size_opts).floor.to_s
|
165
|
+
end
|
169
166
|
end
|
170
167
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module DynamicImage
|
4
|
+
class ImageReader
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def read
|
10
|
+
raise DynamicImage::Errors::InvalidHeader unless valid_header?
|
11
|
+
MiniMagick::Image.read(@data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_header?
|
15
|
+
magic_bytes.each do |expr|
|
16
|
+
if (expr.is_a?(Regexp) && file_header =~ /^#{expr}/) ||
|
17
|
+
(expr.is_a?(String) && file_header.start_with?(expr))
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def file_header
|
27
|
+
@file_header ||= StringIO.new(@data, "rb").read(10)
|
28
|
+
end
|
29
|
+
|
30
|
+
def magic_bytes
|
31
|
+
[
|
32
|
+
# GIF
|
33
|
+
"\x47\x49\x46\x38\x37\x61".force_encoding("binary"),
|
34
|
+
"\x47\x49\x46\x38\x39\x61".force_encoding("binary"),
|
35
|
+
# PNG
|
36
|
+
"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a".force_encoding("binary"),
|
37
|
+
# JPEG
|
38
|
+
"\xff\xd8\xff\xdb".force_encoding("binary"),
|
39
|
+
Regexp.new("\xff\xd8\xff\xe0(.*){2}JFIF".force_encoding("binary")),
|
40
|
+
Regexp.new("\xff\xd8\xff\xe1(.*){2}Exif".force_encoding("binary")),
|
41
|
+
"\xff\xd8\xff\xee\x00\x0e".force_encoding("binary"), # Adobe JPEG
|
42
|
+
# TIFF
|
43
|
+
"\x49\x49\x2a\x00".force_encoding("binary"),
|
44
|
+
"\x4d\x4d\x00\x2a".force_encoding("binary"),
|
45
|
+
]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -5,7 +5,7 @@ module DynamicImage
|
|
5
5
|
#
|
6
6
|
# Calculates cropping and fitting for image sizes.
|
7
7
|
class ImageSizing
|
8
|
-
def initialize(record, options={})
|
8
|
+
def initialize(record, options = {})
|
9
9
|
@record = record
|
10
10
|
@uncropped = options[:uncropped] ? true : false
|
11
11
|
end
|
@@ -76,7 +76,7 @@ module DynamicImage
|
|
76
76
|
# sizing.fit(Vector2d(500, 500), upscale: true)
|
77
77
|
# # => Vector2d(500.0, 312.5)
|
78
78
|
#
|
79
|
-
def fit(fit_size, options={})
|
79
|
+
def fit(fit_size, options = {})
|
80
80
|
fit_size = parse_vector(fit_size)
|
81
81
|
require_dimensions!(fit_size) if options[:crop]
|
82
82
|
fit_size = size.fit(fit_size) unless options[:crop]
|
@@ -122,12 +122,10 @@ module DynamicImage
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def parse_vector(v)
|
125
|
-
v.
|
125
|
+
v.is_a?(String) ? str_to_vector(v) : v
|
126
126
|
end
|
127
127
|
|
128
|
-
|
129
|
-
@record
|
130
|
-
end
|
128
|
+
attr_reader :record
|
131
129
|
|
132
130
|
def require_dimensions!(v)
|
133
131
|
raise DynamicImage::Errors::InvalidSizeOptions unless v.x > 0 && v.y > 0
|
@@ -141,7 +139,7 @@ module DynamicImage
|
|
141
139
|
end
|
142
140
|
|
143
141
|
def str_to_vector(str)
|
144
|
-
x, y = str.match(/(\d*)x(\d*)/)[1,2].map(&:to_i)
|
142
|
+
x, y = str.match(/(\d*)x(\d*)/)[1, 2].map(&:to_i)
|
145
143
|
Vector2d.new(x, y)
|
146
144
|
end
|
147
145
|
|
@@ -153,4 +151,4 @@ module DynamicImage
|
|
153
151
|
Vector2d.new(x, y)
|
154
152
|
end
|
155
153
|
end
|
156
|
-
end
|
154
|
+
end
|
@@ -16,27 +16,23 @@ module DynamicImage
|
|
16
16
|
if valid?
|
17
17
|
case metadata[:colorspace]
|
18
18
|
when /rgb/i
|
19
|
-
|
19
|
+
'rgb'
|
20
20
|
when /cmyk/i
|
21
|
-
|
21
|
+
'cmyk'
|
22
22
|
when /gray/i
|
23
|
-
|
23
|
+
'gray'
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
# Returns the content type of the image.
|
29
29
|
def content_type
|
30
|
-
if valid?
|
31
|
-
"image/#{format.downcase}"
|
32
|
-
end
|
30
|
+
"image/#{format.downcase}" if valid?
|
33
31
|
end
|
34
32
|
|
35
33
|
# Returns the dimensions of the image as a vector.
|
36
34
|
def dimensions
|
37
|
-
if valid?
|
38
|
-
Vector2d.new(*metadata[:dimensions])
|
39
|
-
end
|
35
|
+
Vector2d.new(*metadata[:dimensions]) if valid?
|
40
36
|
end
|
41
37
|
|
42
38
|
# Returns the width of the image.
|
@@ -51,14 +47,12 @@ module DynamicImage
|
|
51
47
|
|
52
48
|
# Returns the format of the image.
|
53
49
|
def format
|
54
|
-
if valid?
|
55
|
-
metadata[:format]
|
56
|
-
end
|
50
|
+
metadata[:format] if valid?
|
57
51
|
end
|
58
52
|
|
59
53
|
# Returns true if the image is valid.
|
60
54
|
def valid?
|
61
|
-
@data && metadata != :invalid
|
55
|
+
@data && reader.valid_header? && metadata != :invalid
|
62
56
|
end
|
63
57
|
|
64
58
|
private
|
@@ -67,18 +61,28 @@ module DynamicImage
|
|
67
61
|
@metadata ||= read_metadata
|
68
62
|
end
|
69
63
|
|
70
|
-
def
|
71
|
-
image =
|
64
|
+
def read_image
|
65
|
+
image = reader.read
|
72
66
|
image.auto_orient
|
73
|
-
|
74
|
-
colorspace: image[:colorspace],
|
75
|
-
dimensions: image[:dimensions],
|
76
|
-
format: image[:format]
|
77
|
-
}
|
67
|
+
result = yield image
|
78
68
|
image.destroy!
|
79
|
-
|
69
|
+
result
|
80
70
|
rescue MiniMagick::Invalid
|
81
71
|
:invalid
|
82
72
|
end
|
73
|
+
|
74
|
+
def reader
|
75
|
+
@reader ||= DynamicImage::ImageReader.new(@data)
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_metadata
|
79
|
+
read_image do |image|
|
80
|
+
{
|
81
|
+
colorspace: image[:colorspace],
|
82
|
+
dimensions: image[:dimensions],
|
83
|
+
format: image[:format]
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
83
87
|
end
|
84
|
-
end
|
88
|
+
end
|
@@ -5,7 +5,6 @@ module DynamicImage
|
|
5
5
|
# = DynamicImage Model Dimensions
|
6
6
|
#
|
7
7
|
module Dimensions
|
8
|
-
|
9
8
|
# Returns the crop gravity.
|
10
9
|
#
|
11
10
|
# DynamicImage will try to keep the pixel represented by
|
@@ -33,9 +32,7 @@ module DynamicImage
|
|
33
32
|
|
34
33
|
# Returns the crop size, or nil if no cropping is applied.
|
35
34
|
def crop_size
|
36
|
-
if crop_size?
|
37
|
-
vector(crop_width, crop_height)
|
38
|
-
end
|
35
|
+
vector(crop_width, crop_height) if crop_size?
|
39
36
|
end
|
40
37
|
|
41
38
|
# Returns true if crop size has been set.
|
@@ -64,9 +61,7 @@ module DynamicImage
|
|
64
61
|
|
65
62
|
# Returns the real size of the image, without any cropping applied.
|
66
63
|
def real_size
|
67
|
-
if real_size?
|
68
|
-
vector(real_width, real_height)
|
69
|
-
end
|
64
|
+
vector(real_width, real_height) if real_size?
|
70
65
|
end
|
71
66
|
|
72
67
|
# Returns true if the size has been set.
|
@@ -92,4 +87,4 @@ module DynamicImage
|
|
92
87
|
end
|
93
88
|
end
|
94
89
|
end
|
95
|
-
end
|
90
|
+
end
|
@@ -13,34 +13,34 @@ module DynamicImage
|
|
13
13
|
validates_data_presence
|
14
14
|
|
15
15
|
validates :colorspace,
|
16
|
-
|
17
|
-
|
16
|
+
presence: true,
|
17
|
+
inclusion: { in: allowed_colorspaces }
|
18
18
|
|
19
19
|
validates :content_type,
|
20
|
-
|
21
|
-
|
20
|
+
presence: true,
|
21
|
+
inclusion: { in: allowed_content_types }
|
22
22
|
|
23
23
|
validates :content_length,
|
24
|
-
|
25
|
-
|
24
|
+
presence: true,
|
25
|
+
numericality: { greater_than: 0, only_integer: true }
|
26
26
|
|
27
27
|
validates :filename,
|
28
|
-
|
29
|
-
|
28
|
+
presence: true,
|
29
|
+
length: { maximum: 255 }
|
30
30
|
|
31
31
|
validates :real_width, :real_height,
|
32
|
-
|
32
|
+
numericality: { greater_than: 0, only_integer: true }
|
33
33
|
|
34
34
|
validates :real_width, :real_height,
|
35
|
-
|
35
|
+
numericality: { greater_than: 0, only_integer: true }
|
36
36
|
|
37
37
|
validates :crop_width, :crop_height,
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
:crop_gravity_x, :crop_gravity_y,
|
39
|
+
numericality: { greater_than: 0, only_integer: true },
|
40
|
+
allow_nil: true
|
41
41
|
|
42
42
|
validates :real_width, :real_height,
|
43
|
-
|
43
|
+
presence: true
|
44
44
|
|
45
45
|
validates :crop_width, presence: true, if: :crop_height?
|
46
46
|
validates :crop_height, presence: true, if: :crop_width?
|
@@ -57,21 +57,21 @@ module DynamicImage
|
|
57
57
|
|
58
58
|
module ClassMethods
|
59
59
|
def allowed_colorspaces
|
60
|
-
%w
|
60
|
+
%w(
|
61
61
|
rgb
|
62
62
|
cmyk
|
63
63
|
gray
|
64
|
-
|
64
|
+
)
|
65
65
|
end
|
66
66
|
|
67
67
|
def allowed_content_types
|
68
|
-
%w
|
68
|
+
%w(
|
69
69
|
image/gif
|
70
70
|
image/jpeg
|
71
71
|
image/pjpeg
|
72
72
|
image/png
|
73
73
|
image/tiff
|
74
|
-
|
74
|
+
)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -80,15 +80,13 @@ module DynamicImage
|
|
80
80
|
def validate_crop_bounds
|
81
81
|
required_size = crop_start + crop_size
|
82
82
|
if required_size.x > real_size.x || required_size.y > real_size.y
|
83
|
-
|
83
|
+
errors.add(:crop_size, 'is out of bounds')
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
87
|
def validate_image
|
88
|
-
unless valid_image?
|
89
|
-
self.errors.add(:data, :invalid)
|
90
|
-
end
|
88
|
+
errors.add(:data, :invalid) unless valid_image?
|
91
89
|
end
|
92
90
|
end
|
93
91
|
end
|
94
|
-
end
|
92
|
+
end
|
data/lib/dynamic_image/model.rb
CHANGED
@@ -70,17 +70,17 @@ module DynamicImage
|
|
70
70
|
|
71
71
|
# Returns true if the image is in the CMYK colorspace
|
72
72
|
def cmyk?
|
73
|
-
colorspace ==
|
73
|
+
colorspace == 'cmyk'
|
74
74
|
end
|
75
75
|
|
76
76
|
# Returns true if the image is in the grayscale colorspace
|
77
77
|
def gray?
|
78
|
-
colorspace ==
|
78
|
+
colorspace == 'gray'
|
79
79
|
end
|
80
80
|
|
81
81
|
# Returns true if the image is in the RGB colorspace
|
82
82
|
def rgb?
|
83
|
-
colorspace ==
|
83
|
+
colorspace == 'rgb'
|
84
84
|
end
|
85
85
|
|
86
86
|
# Finds a web safe content type. GIF, JPEG and PNG images are allowed,
|
@@ -102,15 +102,14 @@ module DynamicImage
|
|
102
102
|
private
|
103
103
|
|
104
104
|
def read_image_metadata
|
105
|
-
metadata = DynamicImage::Metadata.new(
|
105
|
+
metadata = DynamicImage::Metadata.new(data)
|
106
|
+
@valid_image = false
|
106
107
|
if metadata.valid?
|
107
108
|
self.colorspace = metadata.colorspace
|
108
109
|
self.real_width = metadata.width
|
109
110
|
self.real_height = metadata.height
|
110
111
|
self.content_type = metadata.content_type
|
111
112
|
@valid_image = true
|
112
|
-
else
|
113
|
-
@valid_image = false
|
114
113
|
end
|
115
114
|
true
|
116
115
|
end
|
@@ -120,11 +119,11 @@ module DynamicImage
|
|
120
119
|
end
|
121
120
|
|
122
121
|
def safe_content_types
|
123
|
-
%w
|
122
|
+
%w(
|
124
123
|
image/png
|
125
124
|
image/gif
|
126
125
|
image/jpeg
|
127
|
-
|
126
|
+
)
|
128
127
|
end
|
129
128
|
end
|
130
|
-
end
|
129
|
+
end
|
@@ -6,11 +6,11 @@ module DynamicImage
|
|
6
6
|
# Handles all processing of images. Takes an instance of
|
7
7
|
# +DynamicImage::Model+ as argument.
|
8
8
|
class ProcessedImage
|
9
|
-
def initialize(record, options={})
|
9
|
+
def initialize(record, options = {})
|
10
10
|
@record = record
|
11
11
|
@uncropped = options[:uncropped] ? true : false
|
12
12
|
@format = options[:format].to_s.upcase if options[:format]
|
13
|
-
@format =
|
13
|
+
@format = 'JPEG' if defined?(@format) && @format == 'JPG'
|
14
14
|
end
|
15
15
|
|
16
16
|
# Returns the content type of the processed image.
|
@@ -57,7 +57,7 @@ module DynamicImage
|
|
57
57
|
# jpg_data = processed.normalized
|
58
58
|
#
|
59
59
|
# Returns a binary string.
|
60
|
-
def normalized
|
60
|
+
def normalized
|
61
61
|
require_valid_image!
|
62
62
|
process_data do |image|
|
63
63
|
image.combine_options do |combined|
|
@@ -75,7 +75,7 @@ module DynamicImage
|
|
75
75
|
def coalesced(image)
|
76
76
|
if gif?
|
77
77
|
image.coalesce
|
78
|
-
image =
|
78
|
+
image = DynamicImage::ImageReader.new(image.to_blob).read
|
79
79
|
end
|
80
80
|
image
|
81
81
|
end
|
@@ -89,7 +89,8 @@ module DynamicImage
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def image_sizing
|
92
|
-
@image_sizing ||= DynamicImage::ImageSizing.new(record,
|
92
|
+
@image_sizing ||= DynamicImage::ImageSizing.new(record,
|
93
|
+
uncropped: @uncropped)
|
93
94
|
end
|
94
95
|
|
95
96
|
def needs_colorspace_conversion?
|
@@ -101,23 +102,19 @@ module DynamicImage
|
|
101
102
|
end
|
102
103
|
|
103
104
|
def optimize(image)
|
104
|
-
if gif?
|
105
|
-
image.layers 'optimize'
|
106
|
-
end
|
105
|
+
image.layers 'optimize' if gif?
|
107
106
|
image.strip
|
108
107
|
end
|
109
108
|
|
110
|
-
def process_data
|
111
|
-
image = coalesced(
|
109
|
+
def process_data
|
110
|
+
image = coalesced(DynamicImage::ImageReader.new(record.data).read)
|
112
111
|
yield(image)
|
113
112
|
result = image.to_blob
|
114
113
|
image.destroy!
|
115
114
|
result
|
116
115
|
end
|
117
116
|
|
118
|
-
|
119
|
-
@record
|
120
|
-
end
|
117
|
+
attr_reader :record
|
121
118
|
|
122
119
|
def record_format
|
123
120
|
case record.content_type
|
@@ -133,9 +130,7 @@ module DynamicImage
|
|
133
130
|
end
|
134
131
|
|
135
132
|
def require_valid_image!
|
136
|
-
unless record.valid?
|
137
|
-
raise DynamicImage::Errors::InvalidImage
|
138
|
-
end
|
133
|
+
raise DynamicImage::Errors::InvalidImage unless record.valid?
|
139
134
|
end
|
140
135
|
end
|
141
136
|
end
|
@@ -9,7 +9,7 @@ module DynamicImage
|
|
9
9
|
# Declares an image resource.
|
10
10
|
#
|
11
11
|
# image_resources :avatars
|
12
|
-
def image_resources(resource_name, options={})
|
12
|
+
def image_resources(resource_name, options = {})
|
13
13
|
options = {
|
14
14
|
path: "#{resource_name}/:digest(/:size)",
|
15
15
|
constraints: { size: /\d+x\d+/ },
|
data/lib/dynamic_image.rb
CHANGED
@@ -9,6 +9,7 @@ require 'dynamic_image/controller'
|
|
9
9
|
require 'dynamic_image/digest_verifier'
|
10
10
|
require 'dynamic_image/errors'
|
11
11
|
require 'dynamic_image/helper'
|
12
|
+
require 'dynamic_image/image_reader'
|
12
13
|
require 'dynamic_image/image_sizing'
|
13
14
|
require 'dynamic_image/metadata'
|
14
15
|
require 'dynamic_image/model'
|
@@ -6,7 +6,7 @@ require 'rails/generators/rails/resource/resource_generator'
|
|
6
6
|
module DynamicImage
|
7
7
|
module Generators
|
8
8
|
class ResourceGenerator < Rails::Generators::ResourceGenerator
|
9
|
-
desc
|
9
|
+
desc 'Creates a DynamicImage resource'
|
10
10
|
|
11
11
|
def initialize(args, *options)
|
12
12
|
super(inject_dynamic_image_attributes(args), *options)
|
@@ -14,14 +14,14 @@ module DynamicImage
|
|
14
14
|
|
15
15
|
def add_controller_extension
|
16
16
|
inject_into_file(
|
17
|
-
File.join(
|
17
|
+
File.join(
|
18
|
+
'app/controllers',
|
19
|
+
class_path,
|
20
|
+
"#{file_name.pluralize}_controller.rb"),
|
18
21
|
after: "ApplicationController\n"
|
19
22
|
) do
|
20
|
-
" include DynamicImage::Controller\n\n"
|
21
|
-
|
22
|
-
" def model\n" +
|
23
|
-
" #{class_name}\n" +
|
24
|
-
" end\n"
|
23
|
+
" include DynamicImage::Controller\n\n private\n\n" \
|
24
|
+
" def model\n #{class_name}\n end\n"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -46,29 +46,24 @@ module DynamicImage
|
|
46
46
|
|
47
47
|
def inject_dynamic_image_attributes(args)
|
48
48
|
if args.any?
|
49
|
-
|
49
|
+
[args[0]] + dynamic_image_attributes + args[1..args.length]
|
50
50
|
else
|
51
51
|
args
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
55
|
def dynamic_image_attributes
|
56
|
-
%w
|
57
|
-
content_hash:string
|
58
|
-
content_type:string
|
56
|
+
%w(
|
57
|
+
content_hash:string content_type:string
|
59
58
|
content_length:integer
|
60
59
|
filename:string
|
61
60
|
colorspace:string
|
62
|
-
real_width:integer
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
crop_start_y:integer
|
68
|
-
crop_gravity_x:integer
|
69
|
-
crop_gravity_y:integer
|
70
|
-
}
|
61
|
+
real_width:integer real_height:integer
|
62
|
+
crop_width:integer crop_height:integer
|
63
|
+
crop_start_x:integer crop_start_y:integer
|
64
|
+
crop_gravity_x:integer crop_gravity_y:integer
|
65
|
+
)
|
71
66
|
end
|
72
67
|
end
|
73
68
|
end
|
74
|
-
end
|
69
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamic_image
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Inge Jørgensen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/dynamic_image/digest_verifier.rb
|
117
117
|
- lib/dynamic_image/errors.rb
|
118
118
|
- lib/dynamic_image/helper.rb
|
119
|
+
- lib/dynamic_image/image_reader.rb
|
119
120
|
- lib/dynamic_image/image_sizing.rb
|
120
121
|
- lib/dynamic_image/metadata.rb
|
121
122
|
- lib/dynamic_image/model.rb
|