ruby-thumbor 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +83 -0
- data/lib/ruby-thumbor.rb +2 -0
- data/lib/thumbor/cascade.rb +28 -20
- data/lib/thumbor/crypto_url.rb +156 -180
- data/lib/thumbor/version.rb +3 -1
- data/spec/spec_helper.rb +18 -6
- data/spec/thumbor/cascade_spec.rb +70 -70
- metadata +53 -29
- data/README.rdoc +0 -106
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f47258a05c0f11cbb6204f06e463613a467ea0dcadfddd971330de2d58202ed
|
4
|
+
data.tar.gz: c510bda2d4a88fdadb96ff5d6e8051070bc80834b0b1c838a1091596f7a4db14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b886a1f96d8a9703c24e338a7e469686a05283098bc7bdc9dc9460fd753a233d0afd78df66c3b9e6f5ad344b8d5f87ef113d1d0299c81ade419910d7ca7da36
|
7
|
+
data.tar.gz: 1a1603bc5c9851b4faec33c45852890152d497c01be0c9465c41aa9a0cc3d7b3f17d0aaf156bf4592ecf85b75a30a2d1f839c5600065cb5d96484ef31b636a0f
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# ruby-thumbor [![Github Actions](https://github.com/thumbor/ruby-thumbor/actions/workflows/main.yml/badge.svg)](https://github.com/thumbor/ruby-thumbor/actions) [![Gem Version](https://badge.fury.io/rb/ruby-thumbor.svg)](https://rubygems.org/gems/ruby-thumbor) [![Coverage Status](https://coveralls.io/repos/thumbor/ruby-thumbor/badge.svg?branch=master&service=github)](https://coveralls.io/github/thumbor/ruby-thumbor?branch=master)
|
2
|
+
|
3
|
+
ruby-thumbor is the Ruby client to the thumbor imaging service (http://github.com/thumbor/thumbor).
|
4
|
+
|
5
|
+
## Features
|
6
|
+
* Generate thumbor encrypted URLs
|
7
|
+
* Obtain metadata from image operations in thumbor
|
8
|
+
|
9
|
+
## Install
|
10
|
+
``` bash
|
11
|
+
gem install ruby-thumbor
|
12
|
+
```
|
13
|
+
|
14
|
+
## Using it
|
15
|
+
``` ruby
|
16
|
+
require 'ruby-thumbor'
|
17
|
+
|
18
|
+
image = Thumbor::Cascade.new('my-security-key', 'remote-image.com/path/to/image.jpg')
|
19
|
+
image.width(300).height(200).watermark_filter('http://remote-image.com/path/to/image.jpg', 30).generate
|
20
|
+
|
21
|
+
# url will contain something like:
|
22
|
+
# /2913921in321n3k2nj32hjhj3h22/remote-image.com/path/to/image.jpg
|
23
|
+
|
24
|
+
# Now you just need to concatenate this generated path, with your thumbor server url
|
25
|
+
```
|
26
|
+
|
27
|
+
### or
|
28
|
+
``` ruby
|
29
|
+
require 'ruby-thumbor'
|
30
|
+
|
31
|
+
crypto = Thumbor::CryptoURL.new 'my-security-key'
|
32
|
+
|
33
|
+
url = crypto.generate(width: 200, height: 300, image: 'remote-image.com/path/to/image.jpg')
|
34
|
+
```
|
35
|
+
|
36
|
+
## Available Thumbor arguments
|
37
|
+
``` ruby
|
38
|
+
meta: bool # flag that indicates that thumbor should return only meta-data on the operations it would otherwise perform;
|
39
|
+
|
40
|
+
crop: [<int>, <int>, <int>, <int>] # Coordinates for manual cropping. The first item is the two arguments are the coordinates for the left, top point and the last two are the coordinates for the right, bottom point (thus forming the square to crop);
|
41
|
+
|
42
|
+
width: <int> # the width for the thumbnail;
|
43
|
+
|
44
|
+
height: <int> # the height for the thumbnail;
|
45
|
+
|
46
|
+
flip: <bool> # flag that indicates that thumbor should flip horizontally (on the vertical axis) the image;
|
47
|
+
|
48
|
+
flop: <bool> # flag that indicates that thumbor should flip vertically (on the horizontal axis) the image;
|
49
|
+
|
50
|
+
halign: :left, :center or :right # horizontal alignment that thumbor should use for cropping;
|
51
|
+
|
52
|
+
valign: :top, :middle or :bottom # horizontal alignment that thumbor should use for cropping;
|
53
|
+
|
54
|
+
smart: <bool> # flag that indicates that thumbor should use smart cropping;
|
55
|
+
|
56
|
+
filters: ['blur(20)', 'watermark(http://my.site.com/img.png,-10,-10,50)'] # array of filters and their arguments
|
57
|
+
```
|
58
|
+
|
59
|
+
If you need more info on what each option does, check thumbor's documentation at https://thumbor.readthedocs.io/en/latest/index.html.
|
60
|
+
|
61
|
+
## License
|
62
|
+
(The MIT License)
|
63
|
+
|
64
|
+
Copyright (c) 2022 Bernardo Heynemann <heynemann@gmail.com>
|
65
|
+
|
66
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
67
|
+
a copy of this software and associated documentation files (the
|
68
|
+
'Software'), to deal in the Software without restriction, including
|
69
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
70
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
71
|
+
permit persons to whom the Software is furnished to do so, subject to
|
72
|
+
the following conditions:
|
73
|
+
|
74
|
+
The above copyright notice and this permission notice shall be
|
75
|
+
included in all copies or substantial portions of the Software.
|
76
|
+
|
77
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
78
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
79
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
80
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
81
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
82
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
83
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/ruby-thumbor.rb
CHANGED
data/lib/thumbor/cascade.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
require 'openssl'
|
3
5
|
require 'base64'
|
@@ -8,14 +10,17 @@ module Thumbor
|
|
8
10
|
class Cascade
|
9
11
|
attr_accessor :image, :crypto, :options, :filters
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
FILTER_REGEX = Regexp.new('^(.+)_filter$')
|
14
|
+
|
15
|
+
@available_options = %i[
|
16
|
+
meta crop center
|
17
|
+
original_width original_height
|
18
|
+
width height flip
|
19
|
+
flop halign valign
|
20
|
+
smart fit_in adaptive_fit_in
|
21
|
+
full_fit_in adaptive_full_fit_in
|
22
|
+
trim debug
|
23
|
+
]
|
19
24
|
|
20
25
|
extend Forwardable
|
21
26
|
|
@@ -29,7 +34,7 @@ module Thumbor
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
def initialize(key=
|
37
|
+
def initialize(key = nil, image = nil)
|
33
38
|
@key = key
|
34
39
|
@image = image
|
35
40
|
@options = {}
|
@@ -38,36 +43,39 @@ module Thumbor
|
|
38
43
|
end
|
39
44
|
|
40
45
|
def generate
|
41
|
-
@crypto.generate prepare_options(@options).merge({image: @image, filters: @filters})
|
46
|
+
@crypto.generate prepare_options(@options).merge({ image: @image, filters: @filters })
|
42
47
|
end
|
43
48
|
|
44
|
-
def method_missing(
|
45
|
-
if
|
46
|
-
@filters << "#{
|
49
|
+
def method_missing(method, *args)
|
50
|
+
if method =~ FILTER_REGEX
|
51
|
+
@filters << "#{FILTER_REGEX.match(method)[1]}(#{escape_args(args).join(',')})"
|
47
52
|
self
|
48
53
|
else
|
49
54
|
super
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
58
|
+
def respond_to_missing?(method, include_private = false)
|
59
|
+
method =~ FILTER_REGEX || super
|
60
|
+
end
|
61
|
+
|
53
62
|
private
|
54
63
|
|
55
64
|
def escape_args(args)
|
56
65
|
args.map do |arg|
|
57
|
-
arg = CGI
|
66
|
+
arg = CGI.escape(arg) if arg.is_a?(String) && arg.match(%r{^https?://})
|
58
67
|
arg
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
62
71
|
def prepare_options(options)
|
63
|
-
options.
|
72
|
+
options.each_with_object({}) do |item, final_options|
|
64
73
|
value = if item[1].length == 1
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
item[1].first
|
75
|
+
else
|
76
|
+
item[1]
|
77
|
+
end
|
69
78
|
final_options[item[0]] = value
|
70
|
-
final_options
|
71
79
|
end
|
72
80
|
end
|
73
81
|
end
|
data/lib/thumbor/crypto_url.rb
CHANGED
@@ -1,216 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'base64'
|
3
5
|
require 'digest/md5'
|
4
6
|
require 'cgi'
|
5
7
|
|
6
8
|
module Thumbor
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@key = key
|
12
|
-
end
|
9
|
+
class CryptoURL
|
10
|
+
def initialize(key = nil)
|
11
|
+
@key = key
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def calculate_width_and_height(url_parts, options)
|
15
|
+
width = options[:width]
|
16
|
+
height = options[:height]
|
17
|
+
|
18
|
+
width *= -1 if width && options[:flip]
|
19
|
+
height *= -1 if height && options[:flop]
|
20
|
+
|
21
|
+
if width || height
|
22
|
+
width ||= 0
|
23
|
+
height ||= 0
|
24
|
+
end
|
25
|
+
|
26
|
+
has_width = width
|
27
|
+
has_height = height
|
28
|
+
if options[:flip] && !has_width && !has_height
|
29
|
+
width = '-0'
|
30
|
+
height = '0' if !has_height && !(options[:flop])
|
31
|
+
end
|
32
|
+
if options[:flop] && !has_width && !has_height
|
33
|
+
height = '-0'
|
34
|
+
width = '0' if !has_width && !(options[:flip])
|
35
|
+
end
|
36
|
+
|
37
|
+
url_parts.push("#{width}x#{height}") if width || height
|
38
|
+
end
|
17
39
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
def calculate_centered_crop(options)
|
41
|
+
width = options[:width]
|
42
|
+
height = options[:height]
|
43
|
+
original_width = options[:original_width]
|
44
|
+
original_height = options[:original_height]
|
45
|
+
center = options[:center]
|
46
|
+
|
47
|
+
return unless original_width &&
|
48
|
+
original_height &&
|
49
|
+
center &&
|
50
|
+
(width || height)
|
51
|
+
|
52
|
+
raise 'center must be an array of x,y' unless center.is_a?(Array) && center.length == 2
|
53
|
+
|
54
|
+
center_x, center_y = center
|
55
|
+
width ||= original_width
|
56
|
+
height ||= original_height
|
57
|
+
width = width.abs
|
58
|
+
height = height.abs
|
59
|
+
new_aspect_ratio = width / height.to_f
|
60
|
+
original_aspect_ratio = original_width / original_height.to_f
|
61
|
+
|
62
|
+
crop = nil
|
63
|
+
# We're going wider, vertical crop
|
64
|
+
if new_aspect_ratio > original_aspect_ratio
|
65
|
+
# How tall should we be? because new_aspect_ratio is > original_aspect_ratio we can be sure
|
66
|
+
# that cropped_height is always less than original_height (original). This is assumed below.
|
67
|
+
cropped_height = (original_width / new_aspect_ratio).round
|
68
|
+
# Calculate coordinates around center
|
69
|
+
top_crop_point = (center_y - (cropped_height * 0.5)).round
|
70
|
+
bottom_crop_point = (center_y + (cropped_height * 0.5)).round
|
71
|
+
|
72
|
+
# If we've gone above the top of the image, take all from the bottom
|
73
|
+
if top_crop_point.negative?
|
74
|
+
top_crop_point = 0
|
75
|
+
bottom_crop_point = cropped_height
|
76
|
+
# If we've gone below the top of the image, take all from the top
|
77
|
+
elsif bottom_crop_point > original_height
|
78
|
+
top_crop_point = original_height - cropped_height
|
79
|
+
bottom_crop_point = original_height
|
80
|
+
# Because cropped_height < original_height, top_crop_point and
|
81
|
+
# bottom_crop_point will never both be out of bounds
|
46
82
|
end
|
47
83
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
new_aspect_ratio = width / height.to_f
|
70
|
-
original_aspect_ratio = original_width/original_height.to_f
|
71
|
-
|
72
|
-
crop = nil
|
73
|
-
# We're going wider, vertical crop
|
74
|
-
if new_aspect_ratio > original_aspect_ratio
|
75
|
-
# How tall should we be? because new_aspect_ratio is > original_aspect_ratio we can be sure
|
76
|
-
# that cropped_height is always less than original_height (original). This is assumed below.
|
77
|
-
cropped_height = (original_width / new_aspect_ratio).round
|
78
|
-
# Calculate coordinates around center
|
79
|
-
top_crop_point = (center_y - (cropped_height * 0.5)).round
|
80
|
-
bottom_crop_point = (center_y + (cropped_height * 0.5)).round
|
81
|
-
|
82
|
-
# If we've gone above the top of the image, take all from the bottom
|
83
|
-
if top_crop_point < 0
|
84
|
-
top_crop_point = 0
|
85
|
-
bottom_crop_point = cropped_height
|
86
|
-
# If we've gone below the top of the image, take all from the top
|
87
|
-
elsif bottom_crop_point > original_height
|
88
|
-
top_crop_point = original_height - cropped_height
|
89
|
-
bottom_crop_point = original_height
|
90
|
-
# Because cropped_height < original_height, top_crop_point and
|
91
|
-
# bottom_crop_point will never both be out of bounds
|
92
|
-
end
|
93
|
-
|
94
|
-
# Put it together
|
95
|
-
crop = [0, top_crop_point, original_width, bottom_crop_point]
|
96
|
-
# We're going taller, horizontal crop
|
97
|
-
elsif new_aspect_ratio < original_aspect_ratio
|
98
|
-
# How wide should we be? because new_aspect_ratio is < original_aspect_ratio we can be sure
|
99
|
-
# that cropped_width is always less than original_width (original). This is assumed below.
|
100
|
-
cropped_width = (original_height * new_aspect_ratio).round
|
101
|
-
# Calculate coordinates around center
|
102
|
-
left_crop_point = (center_x - (cropped_width * 0.5)).round
|
103
|
-
right_crop_point = (center_x + (cropped_width * 0.5)).round
|
104
|
-
|
105
|
-
# If we've gone beyond the left of the image, take all from the right
|
106
|
-
if left_crop_point < 0
|
107
|
-
left_crop_point = 0
|
108
|
-
right_crop_point = cropped_width
|
109
|
-
# If we've gone beyond the right of the image, take all from the left
|
110
|
-
elsif right_crop_point > original_width
|
111
|
-
left_crop_point = original_width - cropped_width
|
112
|
-
right_crop_point = original_width
|
113
|
-
# Because cropped_width < original_width, left_crop_point and
|
114
|
-
# right_crop_point will never both be out of bounds
|
115
|
-
end
|
116
|
-
|
117
|
-
# Put it together
|
118
|
-
crop = [left_crop_point, 0, right_crop_point, original_height]
|
119
|
-
end
|
120
|
-
|
121
|
-
options[:crop] = crop
|
84
|
+
# Put it together
|
85
|
+
crop = [0, top_crop_point, original_width, bottom_crop_point]
|
86
|
+
# We're going taller, horizontal crop
|
87
|
+
elsif new_aspect_ratio < original_aspect_ratio
|
88
|
+
# How wide should we be? because new_aspect_ratio is < original_aspect_ratio we can be sure
|
89
|
+
# that cropped_width is always less than original_width (original). This is assumed below.
|
90
|
+
cropped_width = (original_height * new_aspect_ratio).round
|
91
|
+
# Calculate coordinates around center
|
92
|
+
left_crop_point = (center_x - (cropped_width * 0.5)).round
|
93
|
+
right_crop_point = (center_x + (cropped_width * 0.5)).round
|
94
|
+
|
95
|
+
# If we've gone beyond the left of the image, take all from the right
|
96
|
+
if left_crop_point.negative?
|
97
|
+
left_crop_point = 0
|
98
|
+
right_crop_point = cropped_width
|
99
|
+
# If we've gone beyond the right of the image, take all from the left
|
100
|
+
elsif right_crop_point > original_width
|
101
|
+
left_crop_point = original_width - cropped_width
|
102
|
+
right_crop_point = original_width
|
103
|
+
# Because cropped_width < original_width, left_crop_point and
|
104
|
+
# right_crop_point will never both be out of bounds
|
122
105
|
end
|
123
106
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
107
|
+
# Put it together
|
108
|
+
crop = [left_crop_point, 0, right_crop_point, original_height]
|
109
|
+
end
|
128
110
|
|
129
|
-
|
111
|
+
options[:crop] = crop
|
112
|
+
end
|
130
113
|
|
131
|
-
|
132
|
-
|
133
|
-
end
|
114
|
+
def url_for(options)
|
115
|
+
raise 'image is a required argument.' unless options[:image]
|
134
116
|
|
135
|
-
|
136
|
-
trim_options = ['trim']
|
137
|
-
trim_options << options[:trim] unless options[:trim] == true or options[:trim][0] == true
|
138
|
-
url_parts.push(trim_options.join(':'))
|
139
|
-
end
|
117
|
+
url_parts = []
|
140
118
|
|
141
|
-
|
142
|
-
url_parts.push('meta')
|
143
|
-
end
|
119
|
+
url_parts.push('debug') if options[:debug]
|
144
120
|
|
145
|
-
|
121
|
+
if options[:trim]
|
122
|
+
trim_options = ['trim']
|
123
|
+
trim_options << options[:trim] unless (options[:trim] == true) || (options[:trim][0] == true)
|
124
|
+
url_parts.push(trim_options.join(':'))
|
125
|
+
end
|
146
126
|
|
147
|
-
|
148
|
-
if crop
|
149
|
-
crop_left = crop[0]
|
150
|
-
crop_top = crop[1]
|
151
|
-
crop_right = crop[2]
|
152
|
-
crop_bottom = crop[3]
|
127
|
+
url_parts.push('meta') if options[:meta]
|
153
128
|
|
154
|
-
|
155
|
-
url_parts.push(crop_left.to_s << 'x' << crop_top.to_s << ':' << crop_right.to_s << 'x' << crop_bottom.to_s)
|
156
|
-
end
|
157
|
-
end
|
129
|
+
calculate_centered_crop(options)
|
158
130
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
131
|
+
crop = options[:crop]
|
132
|
+
if crop
|
133
|
+
crop_left = crop[0]
|
134
|
+
crop_top = crop[1]
|
135
|
+
crop_right = crop[2]
|
136
|
+
crop_bottom = crop[3]
|
164
137
|
|
165
|
-
|
166
|
-
|
167
|
-
|
138
|
+
if crop_left.positive? || crop_top.positive? || crop_bottom.positive? || crop_right.positive?
|
139
|
+
url_parts.push(crop_left.to_s << 'x' << crop_top.to_s << ':' << crop_right.to_s << 'x' << crop_bottom.to_s)
|
140
|
+
end
|
141
|
+
end
|
168
142
|
|
169
|
-
|
143
|
+
%i[fit_in adaptive_fit_in full_fit_in adaptive_full_fit_in].each do |fit|
|
144
|
+
url_parts.push(fit.to_s.gsub('_', '-')) if options[fit]
|
145
|
+
end
|
170
146
|
|
171
|
-
|
172
|
-
|
173
|
-
|
147
|
+
if (options.include?(:fit_in) || options.include?(:full_fit_in)) && !(options.include?(:width) || options.include?(:height))
|
148
|
+
raise ArgumentError, 'When using fit-in or full-fit-in, you must specify width and/or height.'
|
149
|
+
end
|
174
150
|
|
175
|
-
|
176
|
-
url_parts.push(options[:valign])
|
177
|
-
end
|
151
|
+
calculate_width_and_height(url_parts, options)
|
178
152
|
|
179
|
-
|
180
|
-
url_parts.push('smart')
|
181
|
-
end
|
153
|
+
url_parts.push(options[:halign]) if options[:halign] && (options[:halign] != :center)
|
182
154
|
|
183
|
-
|
184
|
-
filter_parts = []
|
185
|
-
options[:filters].each do |filter|
|
186
|
-
filter_parts.push(filter)
|
187
|
-
end
|
155
|
+
url_parts.push(options[:valign]) if options[:valign] && (options[:valign] != :middle)
|
188
156
|
|
189
|
-
|
190
|
-
end
|
157
|
+
url_parts.push('smart') if options[:smart]
|
191
158
|
|
192
|
-
|
159
|
+
if options[:filters] && !options[:filters].empty?
|
160
|
+
filter_parts = []
|
161
|
+
options[:filters].each do |filter|
|
162
|
+
filter_parts.push(filter)
|
193
163
|
end
|
194
164
|
|
195
|
-
|
196
|
-
|
197
|
-
end
|
165
|
+
url_parts.push("filters:#{filter_parts.join(':')}")
|
166
|
+
end
|
198
167
|
|
199
|
-
|
200
|
-
|
168
|
+
url_parts.join('/')
|
169
|
+
end
|
201
170
|
|
202
|
-
|
203
|
-
|
171
|
+
def url_safe_base64(str)
|
172
|
+
Base64.encode64(str).gsub('+', '-').gsub('/', '_').gsub!(/\n/, '')
|
173
|
+
end
|
204
174
|
|
205
|
-
|
175
|
+
def generate(options)
|
176
|
+
thumbor_path = []
|
206
177
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
178
|
+
image_options = url_for(options)
|
179
|
+
thumbor_path << "#{image_options}/" unless image_options.empty?
|
180
|
+
|
181
|
+
thumbor_path << options[:image]
|
182
|
+
|
183
|
+
if @key.nil?
|
184
|
+
thumbor_path.insert(0, '/unsafe/')
|
185
|
+
else
|
186
|
+
signature = url_safe_base64(OpenSSL::HMAC.digest('sha1', @key, thumbor_path.join))
|
187
|
+
thumbor_path.insert(0, "/#{signature}/")
|
188
|
+
end
|
189
|
+
thumbor_path.join
|
215
190
|
end
|
191
|
+
end
|
216
192
|
end
|
data/lib/thumbor/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,17 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'simplecov'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
require 'simplecov-lcov'
|
5
|
+
|
6
|
+
SimpleCov::Formatter::LcovFormatter.config do |config|
|
7
|
+
config.report_with_single_file = true
|
8
|
+
config.single_report_path = 'coverage/lcov.info'
|
6
9
|
end
|
7
10
|
|
8
|
-
|
11
|
+
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
|
12
|
+
[
|
13
|
+
SimpleCov::Formatter::HTMLFormatter,
|
14
|
+
SimpleCov::Formatter::LcovFormatter
|
15
|
+
]
|
16
|
+
)
|
9
17
|
|
10
18
|
SimpleCov.start do
|
11
19
|
add_filter '/spec/'
|
12
20
|
end
|
13
21
|
|
22
|
+
SimpleCov.at_exit do
|
23
|
+
SimpleCov.result.format!
|
24
|
+
end
|
25
|
+
|
14
26
|
RSpec.configure do |c|
|
15
|
-
c.filter_run :
|
27
|
+
c.filter_run focus: true
|
16
28
|
c.run_all_when_everything_filtered = true
|
17
29
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'json'
|
3
5
|
require 'ruby-thumbor'
|
@@ -8,311 +10,309 @@ describe Thumbor::Cascade do
|
|
8
10
|
|
9
11
|
subject { Thumbor::Cascade.new key, image_url }
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
it "should create a new instance passing key and keep it" do
|
14
|
-
expect(subject.computed_key).to eq 'my-security-keym'
|
15
|
-
end
|
13
|
+
it 'should raise an error' do
|
14
|
+
expect { subject.god_of_war_crop }.to raise_error(NoMethodError)
|
16
15
|
end
|
17
16
|
|
18
|
-
it 'should
|
19
|
-
expect
|
17
|
+
it 'should respond to filter methods' do
|
18
|
+
expect(subject.respond_to?('quality_filter')).to be_truthy
|
20
19
|
end
|
21
20
|
|
22
21
|
describe '#generate' do
|
23
|
-
|
24
|
-
|
25
|
-
url = Thumbor::Cascade.new(false, image_url).width(300).height(200).generate
|
22
|
+
it 'should create an unsafe url' do
|
23
|
+
url = Thumbor::Cascade.new(nil, image_url).width(300).height(200).generate
|
26
24
|
expect(url).to eq '/unsafe/300x200/my.domain.com/some/image/url.jpg'
|
27
25
|
end
|
28
26
|
|
29
|
-
it
|
27
|
+
it 'should create an url with debug' do
|
30
28
|
url = subject.debug(true).height(200).generate
|
31
29
|
expect(url).to eq '/5_eX4HHQYk81HQVkc1gBIAvPbLo=/debug/0x200/my.domain.com/some/image/url.jpg'
|
32
30
|
end
|
33
31
|
|
34
|
-
it
|
32
|
+
it 'should create a new instance passing key and keep it' do
|
35
33
|
url = subject.width(300).height(200).generate
|
36
34
|
expect(url).to eq '/TQfyd3H36Z3srcNcLOYiM05YNO8=/300x200/my.domain.com/some/image/url.jpg'
|
37
35
|
end
|
38
36
|
|
39
|
-
it
|
37
|
+
it 'should be able to change the Thumbor key' do
|
40
38
|
url1 = subject.width(300).height(200).generate
|
41
39
|
url2 = Thumbor::Cascade.new('another-thumbor-key', image_url).width(300).height(200).generate
|
42
40
|
expect(url1).not_to eq url2
|
43
41
|
end
|
44
42
|
|
45
|
-
it
|
43
|
+
it 'should create a new instance passing key and keep it' do
|
46
44
|
url = subject.width(300).height(200).meta(true).generate
|
47
45
|
expect(url).to eq '/YBQEWd3g_WRMnVEG73zfzcr8Zj0=/meta/300x200/my.domain.com/some/image/url.jpg'
|
48
46
|
end
|
49
47
|
|
50
|
-
it
|
48
|
+
it 'should create a new instance passing key and keep it' do
|
51
49
|
url = subject.width(300).height(200).meta(true).smart(true).generate
|
52
50
|
expect(url).to eq '/jP89J0qOWHgPlm_lOA28GtOh5GU=/meta/300x200/smart/my.domain.com/some/image/url.jpg'
|
53
51
|
end
|
54
52
|
|
55
|
-
it
|
53
|
+
it 'should create a new instance passing key and keep it' do
|
56
54
|
url = subject.width(300).height(200).meta(true).smart(true).fit_in(true).generate
|
57
55
|
expect(url).to eq '/zrrOh_TtTs4kiLLEQq1w4bcTYdc=/meta/fit-in/300x200/smart/my.domain.com/some/image/url.jpg'
|
58
56
|
end
|
59
57
|
|
60
|
-
it
|
58
|
+
it 'should create a new instance passing key and keep it' do
|
61
59
|
url = subject.width(300).height(200).meta(true).smart(true).fit_in(true).flip(true).generate
|
62
60
|
expect(url).to eq '/4t1XK1KH43cOb1QJ9tU00-W2_k8=/meta/fit-in/-300x200/smart/my.domain.com/some/image/url.jpg'
|
63
61
|
end
|
64
62
|
|
65
|
-
it
|
63
|
+
it 'should create a new instance passing key and keep it' do
|
66
64
|
url = subject.width(300).height(200).meta(true).smart(true).fit_in(true).flip(true).flop(true).generate
|
67
65
|
expect(url).to eq '/HJnvjZU69PkPOhyZGu-Z3Uc_W_A=/meta/fit-in/-300x-200/smart/my.domain.com/some/image/url.jpg'
|
68
66
|
end
|
69
67
|
|
70
|
-
it
|
68
|
+
it 'should create a new instance passing key and keep it' do
|
71
69
|
url = subject.quality_filter(20).brightness_filter(10).generate
|
72
70
|
expect(url).to eq '/q0DiFg-5-eFZIqyN3lRoCvg2K0s=/filters:quality(20):brightness(10)/my.domain.com/some/image/url.jpg'
|
73
71
|
end
|
74
72
|
|
75
|
-
it
|
73
|
+
it 'should return just the image hash if no arguments passed' do
|
76
74
|
url = subject.generate
|
77
75
|
expect(url).to eq '/964rCTkAEDtvjy_a572k7kRa0SU=/my.domain.com/some/image/url.jpg'
|
78
76
|
end
|
79
77
|
|
80
|
-
it
|
78
|
+
it 'should raise if no image passed' do
|
81
79
|
expect { Thumbor::Cascade.new.generate }.to raise_error(RuntimeError)
|
82
80
|
end
|
83
81
|
|
84
|
-
it
|
82
|
+
it 'should return proper url for width-only' do
|
85
83
|
url = subject.width(300).generate
|
86
84
|
expect(url).to eq '/eFwrBWryxtRw9hDSbQPhi5iLpo8=/300x0/my.domain.com/some/image/url.jpg'
|
87
85
|
end
|
88
86
|
|
89
|
-
it
|
87
|
+
it 'should return proper url for height-only' do
|
90
88
|
url = subject.height(300).generate
|
91
89
|
expect(url).to eq '/-VGIgp7g8cMKcfF2gFK9ZpmB_5w=/0x300/my.domain.com/some/image/url.jpg'
|
92
90
|
end
|
93
91
|
|
94
|
-
it
|
92
|
+
it 'should return proper url for width and height' do
|
95
93
|
url = subject.width(200).height(300).generate
|
96
94
|
expect(url).to eq '/TrM0qqfcivb6VxS3Hxlxn43W21k=/200x300/my.domain.com/some/image/url.jpg'
|
97
95
|
end
|
98
96
|
|
99
|
-
it
|
97
|
+
it 'should return proper smart url' do
|
100
98
|
url = subject.width(200).height(300).smart(true).generate
|
101
99
|
expect(url).to eq '/hdzhxXzK45DzNTru5urV6x6xxSs=/200x300/smart/my.domain.com/some/image/url.jpg'
|
102
100
|
end
|
103
101
|
|
104
|
-
it
|
102
|
+
it 'should return proper fit-in url' do
|
105
103
|
url = subject.width(200).height(300).fit_in(true).generate
|
106
104
|
expect(url).to eq '/LOv6ArMOJA2VRpfMQjjs4xSyZpM=/fit-in/200x300/my.domain.com/some/image/url.jpg'
|
107
105
|
end
|
108
106
|
|
109
|
-
it
|
107
|
+
it 'should return proper adaptive-fit-in url' do
|
110
108
|
url = subject.width(200).height(300).adaptive_fit_in(true).generate
|
111
109
|
expect(url).to eq '/V2xmSmQZm4i5-0Flx8iuRtawOkg=/adaptive-fit-in/200x300/my.domain.com/some/image/url.jpg'
|
112
110
|
end
|
113
111
|
|
114
|
-
it
|
112
|
+
it 'should return proper full-fit-in url' do
|
115
113
|
url = subject.width(200).height(300).full_fit_in(true).generate
|
116
114
|
expect(url).to eq '/geXhR7ZFxztQTsKzmkDxYCX-HHg=/full-fit-in/200x300/my.domain.com/some/image/url.jpg'
|
117
115
|
end
|
118
116
|
|
119
|
-
it
|
117
|
+
it 'should return proper adaptive-full-fit-in url' do
|
120
118
|
url = subject.width(200).height(300).adaptive_full_fit_in(true).generate
|
121
119
|
expect(url).to eq '/jlUfjdC-6rG6jmuHgFp6eKgPy2g=/adaptive-full-fit-in/200x300/my.domain.com/some/image/url.jpg'
|
122
120
|
end
|
123
121
|
|
124
|
-
[
|
122
|
+
%i[fit_in full_fit_in].each do |fit|
|
125
123
|
it "should raise error when using #{fit} without width or height" do
|
126
124
|
subject.send(fit, true)
|
127
|
-
expect{subject.generate}.to raise_error(ArgumentError)
|
125
|
+
expect { subject.generate }.to raise_error(ArgumentError)
|
128
126
|
end
|
129
127
|
end
|
130
128
|
|
131
|
-
it
|
129
|
+
it 'should return proper flip url if no width and height' do
|
132
130
|
url = subject.flip(true).generate
|
133
131
|
expect(url).to eq '/rlI4clPR-p-PR2QAxj5ZWiTfvH4=/-0x0/my.domain.com/some/image/url.jpg'
|
134
132
|
end
|
135
133
|
|
136
|
-
it
|
134
|
+
it 'should return proper flop url if no width and height' do
|
137
135
|
url = subject.flop(true).generate
|
138
136
|
expect(url).to eq '/-4dmGj-TwIEqTAL9_9yEqUM8cks=/0x-0/my.domain.com/some/image/url.jpg'
|
139
137
|
end
|
140
138
|
|
141
|
-
it
|
139
|
+
it 'should return proper flip-flop url if no width and height' do
|
142
140
|
url = subject.flip(true).flop(true).generate
|
143
141
|
expect(url).to eq '/FnMxpQMmxiMpdG219Dsj8pD_4Xc=/-0x-0/my.domain.com/some/image/url.jpg'
|
144
142
|
end
|
145
143
|
|
146
|
-
it
|
144
|
+
it 'should return proper flip url if width' do
|
147
145
|
url = subject.width(300).flip(true).generate
|
148
146
|
expect(url).to eq '/ccr2PoSYcTEGL4_Wzt4u3wWVRKU=/-300x0/my.domain.com/some/image/url.jpg'
|
149
147
|
end
|
150
148
|
|
151
|
-
it
|
149
|
+
it 'should return proper flop url if height' do
|
152
150
|
url = subject.height(300).flop(true).generate
|
153
151
|
expect(url).to eq '/R5K91tkyNgXO65F6E0txgA6C9lY=/0x-300/my.domain.com/some/image/url.jpg'
|
154
152
|
end
|
155
153
|
|
156
|
-
it
|
154
|
+
it 'should return horizontal align' do
|
157
155
|
url = subject.halign(:left).generate
|
158
156
|
expect(url).to eq '/GTJE3wUt3sURik0O9Nae8sfI928=/left/my.domain.com/some/image/url.jpg'
|
159
157
|
end
|
160
158
|
|
161
|
-
it
|
159
|
+
it 'should not return horizontal align if it is center' do
|
162
160
|
url = subject.halign(:center).generate
|
163
161
|
expect(url).to eq '/964rCTkAEDtvjy_a572k7kRa0SU=/my.domain.com/some/image/url.jpg'
|
164
162
|
end
|
165
163
|
|
166
|
-
it
|
164
|
+
it 'should return vertical align' do
|
167
165
|
url = subject.valign(:top).generate
|
168
166
|
expect(url).to eq '/1QQo5ihObuhgwl95--Z3g78vjiE=/top/my.domain.com/some/image/url.jpg'
|
169
167
|
end
|
170
168
|
|
171
|
-
it
|
169
|
+
it 'should not return vertical align if it is middle' do
|
172
170
|
url = subject.valign(:middle).generate
|
173
171
|
expect(url).to eq '/964rCTkAEDtvjy_a572k7kRa0SU=/my.domain.com/some/image/url.jpg'
|
174
172
|
end
|
175
173
|
|
176
|
-
it
|
174
|
+
it 'should return halign and valign properly' do
|
177
175
|
url = subject.halign(:left).valign(:top).generate
|
178
176
|
expect(url).to eq '/yA2rmtWv_uzHd9klz5OuMIZ5auI=/left/top/my.domain.com/some/image/url.jpg'
|
179
177
|
end
|
180
178
|
|
181
|
-
it
|
179
|
+
it 'should return meta properly' do
|
182
180
|
url = subject.meta(true).generate
|
183
181
|
expect(url).to eq '/WvIJFDJDePgIl5hZcLgtrzIPxfY=/meta/my.domain.com/some/image/url.jpg'
|
184
182
|
end
|
185
183
|
|
186
|
-
it
|
184
|
+
it 'should return proper crop url when param is array' do
|
187
185
|
url = subject.crop([10, 20, 30, 40]).generate
|
188
186
|
expect(url).to eq '/QcnhqNfHwiP6BHLbD6UvneX7K28=/10x20:30x40/my.domain.com/some/image/url.jpg'
|
189
187
|
end
|
190
188
|
|
191
|
-
it
|
189
|
+
it 'should return proper crop url' do
|
192
190
|
url = subject.crop(10, 20, 30, 40).generate
|
193
191
|
expect(url).to eq '/QcnhqNfHwiP6BHLbD6UvneX7K28=/10x20:30x40/my.domain.com/some/image/url.jpg'
|
194
192
|
end
|
195
193
|
|
196
|
-
it
|
194
|
+
it 'should ignore crop if all zeros' do
|
197
195
|
url = subject.crop(0, 0, 0, 0).generate
|
198
196
|
expect(url).to eq '/964rCTkAEDtvjy_a572k7kRa0SU=/my.domain.com/some/image/url.jpg'
|
199
197
|
end
|
200
198
|
|
201
|
-
it
|
199
|
+
it 'should have smart after halign and valign' do
|
202
200
|
url = subject.halign(:left).valign(:top).smart(true).generate
|
203
201
|
expect(url).to eq '/KS6mVuzlGE3hJ75n3JUonfGgSFM=/left/top/smart/my.domain.com/some/image/url.jpg'
|
204
202
|
end
|
205
203
|
|
206
|
-
it
|
204
|
+
it 'should have quality filter' do
|
207
205
|
url = subject.quality_filter(20).generate
|
208
206
|
expect(url).to eq '/NyA-is4NojxiRqo0NcmJDhB6GTs=/filters:quality(20)/my.domain.com/some/image/url.jpg'
|
209
207
|
end
|
210
208
|
|
211
|
-
it
|
209
|
+
it 'should have brightness filter' do
|
212
210
|
url = subject.brightness_filter(30).generate
|
213
211
|
expect(url).to eq '/oXDmnGD7vQV-rXcj8kCl1tcS3jc=/filters:brightness(30)/my.domain.com/some/image/url.jpg'
|
214
212
|
end
|
215
213
|
|
216
|
-
it
|
214
|
+
it 'should have 2 filters' do
|
217
215
|
url = subject.brightness_filter(30).quality_filter(20).generate
|
218
216
|
expect(url).to eq '/SW9o4xQG1QAzE69WzEzarL_G3MI=/filters:brightness(30):quality(20)/my.domain.com/some/image/url.jpg'
|
219
217
|
end
|
220
218
|
|
221
|
-
it
|
219
|
+
it 'should escape url args' do
|
222
220
|
url = subject.watermark_filter('http://my-server.com/image.png', 30).quality_filter(20).generate
|
223
221
|
expect(url).to eq '/4b9kwg0-zsojf7Ed01TPKPYOel4=/filters:watermark(http%3A%2F%2Fmy-server.com%2Fimage.png,30):quality(20)/my.domain.com/some/image/url.jpg'
|
224
222
|
end
|
225
223
|
|
226
|
-
it
|
224
|
+
it 'should have trim without params' do
|
227
225
|
url = subject.trim.generate
|
228
226
|
expect(url).to eq '/w23BC0dUiYBFrUnuoYJe8XROuyw=/trim/my.domain.com/some/image/url.jpg'
|
229
227
|
end
|
230
228
|
|
231
|
-
it
|
229
|
+
it 'should have trim with direction param' do
|
232
230
|
url = subject.trim('bottom-right').generate
|
233
231
|
expect(url).to eq '/kXPwSmqEvPFQezgzBCv9BtPWmBY=/trim:bottom-right/my.domain.com/some/image/url.jpg'
|
234
232
|
end
|
235
233
|
|
236
|
-
it
|
234
|
+
it 'should have trim with direction and tolerance param' do
|
237
235
|
url = subject.trim('bottom-right', 15).generate
|
238
236
|
expect(url).to eq '/TUCEIhtWfI1Uv9zjavCSl_i0A_8=/trim:bottom-right:15/my.domain.com/some/image/url.jpg'
|
239
237
|
end
|
240
238
|
|
241
|
-
it
|
239
|
+
it 'should have the right crop when cropping horizontally and given a left center' do
|
242
240
|
url = subject.original_width(100).original_height(100).width(40).height(50).center(0, 50).generate
|
243
241
|
expect(url).to eq '/SZIT3w4Qgebv5DuVJ8G1IH1mkCU=/0x0:80x100/40x50/my.domain.com/some/image/url.jpg'
|
244
242
|
end
|
245
243
|
|
246
|
-
it
|
244
|
+
it 'should have the right crop when cropping horizontally and given a right center' do
|
247
245
|
url = subject.original_width(100).original_height(100).width(40).height(50).center(100, 50).generate
|
248
246
|
expect(url).to eq '/NEtCYehaISE7qR3zFj15CxnZoCs=/20x0:100x100/40x50/my.domain.com/some/image/url.jpg'
|
249
247
|
end
|
250
248
|
|
251
|
-
it
|
249
|
+
it 'should have the right crop when cropping horizontally and given the actual center' do
|
252
250
|
url = subject.original_width(100).original_height(100).width(40).height(50).center(50, 50).generate
|
253
251
|
expect(url).to eq '/JLH65vJTu6d-cXBmqe5hYoSD4ho=/10x0:90x100/40x50/my.domain.com/some/image/url.jpg'
|
254
252
|
end
|
255
253
|
|
256
|
-
it
|
254
|
+
it 'should have the right crop when cropping vertically and given a top center' do
|
257
255
|
url = subject.original_width(100).original_height(100).width(50).height(40).center(50, 0).generate
|
258
256
|
expect(url).to eq '/FIMZcLatW6bjgSRH9xTkEwUZAZ8=/0x0:100x80/50x40/my.domain.com/some/image/url.jpg'
|
259
257
|
end
|
260
258
|
|
261
|
-
it
|
259
|
+
it 'should have the right crop when cropping vertically and given a bottom center' do
|
262
260
|
url = subject.original_width(100).original_height(100).width(50).height(40).center(50, 100).generate
|
263
261
|
expect(url).to eq '/9Ud0sVo6i9DLOjlKbQP_4JXgFmA=/0x20:100x100/50x40/my.domain.com/some/image/url.jpg'
|
264
262
|
end
|
265
263
|
|
266
|
-
it
|
264
|
+
it 'should have the right crop when cropping vertically and given the actual center' do
|
267
265
|
url = subject.original_width(100).original_height(100).width(50).height(40).center(50, 50).generate
|
268
266
|
expect(url).to eq '/WejLJn8djJLn7DkMUq3S0zZCvZE=/0x10:100x90/50x40/my.domain.com/some/image/url.jpg'
|
269
267
|
end
|
270
268
|
|
271
|
-
it
|
269
|
+
it 'should have the no crop when not necessary' do
|
272
270
|
url = subject.original_width(100).original_height(100).width(50).height(50).center(50, 0).generate
|
273
271
|
expect(url).to eq '/trIjfr513nkGkCpKXK6qgox2jPA=/50x50/my.domain.com/some/image/url.jpg'
|
274
272
|
end
|
275
273
|
|
276
|
-
it
|
277
|
-
expect
|
274
|
+
it 'should blow up with a bad center' do
|
275
|
+
expect do
|
276
|
+
subject.original_width(100).original_height(100).width(50).height(50).center(50).generate
|
277
|
+
end.to raise_error(RuntimeError)
|
278
278
|
end
|
279
279
|
|
280
|
-
it
|
280
|
+
it 'should have no crop with a missing original_height' do
|
281
281
|
url = subject.original_width(100).width(50).height(40).center(50, 0).generate
|
282
282
|
expect(url).to eq '/veYlY0msKmemAaXpeav2kCNftmU=/50x40/my.domain.com/some/image/url.jpg'
|
283
283
|
end
|
284
284
|
|
285
|
-
it
|
285
|
+
it 'should have no crop with a missing original_width' do
|
286
286
|
url = subject.original_height(100).width(50).height(40).center(50, 0).generate
|
287
287
|
expect(url).to eq '/veYlY0msKmemAaXpeav2kCNftmU=/50x40/my.domain.com/some/image/url.jpg'
|
288
288
|
end
|
289
289
|
|
290
|
-
it
|
290
|
+
it 'should have no crop with out a width and height' do
|
291
291
|
url = subject.original_width(100).original_height(100).center(50, 50).generate
|
292
292
|
expect(url).to eq '/964rCTkAEDtvjy_a572k7kRa0SU=/my.domain.com/some/image/url.jpg'
|
293
293
|
end
|
294
294
|
|
295
|
-
it
|
295
|
+
it 'should use the original width with a missing width' do
|
296
296
|
url = subject.original_width(100).original_height(100).height(80).center(50, 50).generate
|
297
297
|
expect(url).to eq '/02BNIIJ9NYNV9Q03JHPtlP0DIDg=/0x10:100x90/0x80/my.domain.com/some/image/url.jpg'
|
298
298
|
end
|
299
299
|
|
300
|
-
it
|
300
|
+
it 'should use the original height with a missing height' do
|
301
301
|
url = subject.original_width(100).original_height(100).width(80).center(50, 50).generate
|
302
302
|
expect(url).to eq '/0XL5BmMi3vlJQfw6aGOVW-M1vVI=/10x0:90x100/80x0/my.domain.com/some/image/url.jpg'
|
303
303
|
end
|
304
304
|
|
305
|
-
it
|
305
|
+
it 'should have the right crop with a negative width' do
|
306
306
|
url = subject.original_width(100).original_height(100).width(-50).height(40).center(50, 50).generate
|
307
307
|
expect(url).to eq '/IuRNPlFlpTVol45bDkOm2PGvneo=/0x10:100x90/-50x40/my.domain.com/some/image/url.jpg'
|
308
308
|
end
|
309
309
|
|
310
|
-
it
|
310
|
+
it 'should have the right crop with a negative height' do
|
311
311
|
url = subject.original_width(100).original_height(100).width(50).height(-40).center(50, 50).generate
|
312
312
|
expect(url).to eq '/-8IhWGEeXaY1uv945i9EHLVjwuk=/0x10:100x90/50x-40/my.domain.com/some/image/url.jpg'
|
313
313
|
end
|
314
314
|
|
315
|
-
it
|
315
|
+
it 'should have the right crop with a negative height and width' do
|
316
316
|
url = subject.original_width(100).original_height(100).width(-50).height(-40).center(50, 50).generate
|
317
317
|
expect(url).to eq '/lfjGLTTEaW_Rcvc1q0ZhfYup2jg=/0x10:100x90/-50x-40/my.domain.com/some/image/url.jpg'
|
318
318
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-thumbor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernardo Heynemann
|
@@ -9,73 +9,88 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '13.0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '13.0'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: rspec
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
17
31
|
requirements:
|
18
|
-
- - "
|
32
|
+
- - "~>"
|
19
33
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
34
|
+
version: '3.10'
|
21
35
|
type: :development
|
22
36
|
prerelease: false
|
23
37
|
version_requirements: !ruby/object:Gem::Requirement
|
24
38
|
requirements:
|
25
|
-
- - "
|
39
|
+
- - "~>"
|
26
40
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
41
|
+
version: '3.10'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
43
|
+
name: rubocop-rspec
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
31
45
|
requirements:
|
32
|
-
- - "
|
46
|
+
- - "~>"
|
33
47
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
48
|
+
version: 2.8.0
|
35
49
|
type: :development
|
36
50
|
prerelease: false
|
37
51
|
version_requirements: !ruby/object:Gem::Requirement
|
38
52
|
requirements:
|
39
|
-
- - "
|
53
|
+
- - "~>"
|
40
54
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
55
|
+
version: 2.8.0
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
57
|
+
name: simplecov
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
45
59
|
requirements:
|
46
|
-
- - "
|
60
|
+
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version:
|
62
|
+
version: 0.21.2
|
49
63
|
type: :development
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
|
-
- - "
|
67
|
+
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version:
|
69
|
+
version: 0.21.2
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
71
|
+
name: simplecov-lcov
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
59
73
|
requirements:
|
60
|
-
- - "
|
74
|
+
- - "~>"
|
61
75
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
76
|
+
version: 0.8.0
|
63
77
|
type: :development
|
64
78
|
prerelease: false
|
65
79
|
version_requirements: !ruby/object:Gem::Requirement
|
66
80
|
requirements:
|
67
|
-
- - "
|
81
|
+
- - "~>"
|
68
82
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
83
|
+
version: 0.8.0
|
70
84
|
description: ruby-thumbor is the client to the thumbor imaging service (http://github.com/thumbor/thumbor).
|
71
85
|
email:
|
72
86
|
- heynemann@gmail.com
|
73
|
-
-
|
87
|
+
- guilherme@souza.tech
|
74
88
|
executables: []
|
75
89
|
extensions: []
|
76
|
-
extra_rdoc_files:
|
90
|
+
extra_rdoc_files:
|
91
|
+
- README.md
|
77
92
|
files:
|
78
|
-
- README.
|
93
|
+
- README.md
|
79
94
|
- lib/ruby-thumbor.rb
|
80
95
|
- lib/thumbor/cascade.rb
|
81
96
|
- lib/thumbor/crypto_url.rb
|
@@ -83,12 +98,22 @@ files:
|
|
83
98
|
- spec/spec_helper.rb
|
84
99
|
- spec/thumbor/cascade_spec.rb
|
85
100
|
homepage: http://github.com/thumbor/ruby-thumbor
|
86
|
-
licenses:
|
87
|
-
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
bug_tracker_uri: https://github.com/thumbor/ruby-thumbor/issues
|
105
|
+
documentation_uri: https://www.rubydoc.info/gems/ruby-thumbor
|
106
|
+
homepage_uri: http://github.com/thumbor/ruby-thumbor
|
107
|
+
source_code_uri: https://github.com/thumbor/ruby-thumbor
|
88
108
|
post_install_message:
|
89
109
|
rdoc_options:
|
110
|
+
- "--title"
|
111
|
+
- Ruby-Thumbor
|
90
112
|
- "--main"
|
91
|
-
- README.
|
113
|
+
- README.md
|
114
|
+
- "--line-numbers"
|
115
|
+
- "--inline-source"
|
116
|
+
- "--quiet"
|
92
117
|
require_paths:
|
93
118
|
- lib
|
94
119
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -102,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
127
|
- !ruby/object:Gem::Version
|
103
128
|
version: '0'
|
104
129
|
requirements: []
|
105
|
-
|
106
|
-
rubygems_version: 2.7.6
|
130
|
+
rubygems_version: 3.3.3
|
107
131
|
signing_key:
|
108
132
|
specification_version: 4
|
109
133
|
summary: ruby-thumbor is the client to the thumbor imaging service (http://github.com/thumbor/thumbor).
|
data/README.rdoc
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
= ruby-thumbor {<img src="https://secure.travis-ci.org/thumbor/ruby-thumbor.png?branch=master" alt="Build Status" />}[http://travis-ci.org/thumbor/ruby-thumbor] {<img src="https://badge.fury.io/rb/ruby-thumbor.svg" alt="Gem Version" />}[http://badge.fury.io/rb/ruby-thumbor] {<img src="https://coveralls.io/repos/thumbor/ruby-thumbor/badge.svg?branch=master&service=github" alt="Coverage Status" />}[https://coveralls.io/github/thumbor/ruby-thumbor?branch=master]
|
2
|
-
|
3
|
-
|
4
|
-
* http://github.com/thumbor/ruby-thumbor
|
5
|
-
|
6
|
-
== DESCRIPTION:
|
7
|
-
|
8
|
-
ruby-thumbor is the client to the thumbor imaging service (http://github.com/thumbor/thumbor).
|
9
|
-
|
10
|
-
== FEATURES:
|
11
|
-
|
12
|
-
* Generate thumbor encrypted URLs
|
13
|
-
* Obtain metadata from image operations in thumbor
|
14
|
-
|
15
|
-
== DEPENDENCIES
|
16
|
-
|
17
|
-
No dependencies required for regular usage.
|
18
|
-
|
19
|
-
* thumbor (http://github.com/thumbor/thumbor) for running ruby-thumbor tests.
|
20
|
-
|
21
|
-
== INSTALL:
|
22
|
-
|
23
|
-
gem install ruby-thumbor
|
24
|
-
|
25
|
-
gem 'ruby-thumbor'
|
26
|
-
|
27
|
-
== BREAKING CHANGE ON 3.0:
|
28
|
-
|
29
|
-
Old image Url isn't supported anymore
|
30
|
-
Switch from #url_for to #generate method
|
31
|
-
|
32
|
-
== USAGE:
|
33
|
-
|
34
|
-
require 'ruby-thumbor'
|
35
|
-
|
36
|
-
crypto = Thumbor::CryptoURL.new 'my-security-key'
|
37
|
-
|
38
|
-
url = crypto.generate :width => 200, :height => 300, :image => 'remote-image.com/path/to/image.jpg'
|
39
|
-
|
40
|
-
# url will contain something like:
|
41
|
-
# /2913921in321n3k2nj32hjhj3h22/remote-image.com/path/to/image.jpg
|
42
|
-
|
43
|
-
# Now you just need to concatenate this generated path, with your thumbor server url
|
44
|
-
|
45
|
-
or
|
46
|
-
|
47
|
-
require 'ruby-thumbor'
|
48
|
-
|
49
|
-
image = Thumbor::Cascade.new('my-security-key', 'remote-image.com/path/to/image.jpg')
|
50
|
-
image.width(300).height(200).watermark_filter('http://remote-image.com/path/to/image.jpg', 30).generate
|
51
|
-
|
52
|
-
Available arguments to the generate method:
|
53
|
-
|
54
|
-
:meta => bool - flag that indicates that thumbor should return only meta-data on the operations it would
|
55
|
-
otherwise perform;
|
56
|
-
|
57
|
-
:crop => [<int>, <int>, <int>, <int>] - Coordinates for manual cropping. The first item is the two arguments are
|
58
|
-
the coordinates for the left, top point and the last two are the coordinates
|
59
|
-
for the right, bottom point (thus forming the square to crop);
|
60
|
-
|
61
|
-
:width => <int> - the width for the thumbnail;
|
62
|
-
|
63
|
-
:height => <int> - the height for the thumbnail;
|
64
|
-
|
65
|
-
:flip => <bool> - flag that indicates that thumbor should flip horizontally (on the vertical axis) the image;
|
66
|
-
|
67
|
-
:flop => <bool> - flag that indicates that thumbor should flip vertically (on the horizontal axis) the image;
|
68
|
-
|
69
|
-
:halign => :left, :center or :right - horizontal alignment that thumbor should use for cropping;
|
70
|
-
|
71
|
-
:valign => :top, :middle or :bottom - horizontal alignment that thumbor should use for cropping;
|
72
|
-
|
73
|
-
:smart => <bool> - flag that indicates that thumbor should use smart cropping;
|
74
|
-
|
75
|
-
:filters => ['blur(20)', 'watermark(http://my.site.com/img.png,-10,-10,50)'] - array of filters and their arguments
|
76
|
-
|
77
|
-
If you need more info on what each option does, check thumbor's documentation at https://github.com/thumbor/thumbor/wiki.
|
78
|
-
|
79
|
-
== CONTRIBUTIONS:
|
80
|
-
|
81
|
-
* Hugo Lopes (hugobr) - Fixes in the usage readme part of the docs.
|
82
|
-
|
83
|
-
== LICENSE:
|
84
|
-
|
85
|
-
(The MIT License)
|
86
|
-
|
87
|
-
Copyright (c) 2011 Bernardo Heynemann <heynemann@gmail.com>
|
88
|
-
|
89
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
90
|
-
a copy of this software and associated documentation files (the
|
91
|
-
'Software'), to deal in the Software without restriction, including
|
92
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
93
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
94
|
-
permit persons to whom the Software is furnished to do so, subject to
|
95
|
-
the following conditions:
|
96
|
-
|
97
|
-
The above copyright notice and this permission notice shall be
|
98
|
-
included in all copies or substantial portions of the Software.
|
99
|
-
|
100
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
101
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
102
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
103
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
104
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
105
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
106
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|