ruby-thumbor 2.0.1 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 619894cedb07f907548f9fd762a236775c7cf224
4
- data.tar.gz: 6e94d8844c1da9d631933334607a948eb4b52958
2
+ SHA256:
3
+ metadata.gz: 1c4062846a0e57d282db5c12f75de306be194a7f85daf6a95b4ad7a45f359590
4
+ data.tar.gz: c81eecd67d7d8c1d1a210b8061cddab4c36d77562861f67f781ea9734ed07164
5
5
  SHA512:
6
- metadata.gz: 74125821b2fd078fef7635c302a3842fbbf2041a2599321acda4cde19ca268c5d74e8b4069de5aaf24e6ab2c87afb5439d64556284944f37b08066502d25d66f
7
- data.tar.gz: ee17011292ba6e85a4c784d854260c348ca10661aee4d5c600f41c82a5c91fe17c5410e0cefe7ca5710ff952722179861419c37a9fd78fa6b35be12fb0d83cec
6
+ metadata.gz: a261f5a06ace027bb296423c3c5e6b63898a107dbb267ac5f90737db9008794e7e2ebc701f3cd7cdc574aab5d48c66d27a555497ca19e4a9ee79fae7757f00e2
7
+ data.tar.gz: d7848f2f8181d33cf077cde11feae042bccf9d9de397c5f79893401380a046863d72affdf72815b0e4700097670d54d11607ba6b1ffe108f96706d6ba6edcbc5
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # ruby-thumbor
2
+ [![Github Actions - tests](https://github.com/thumbor/ruby-thumbor/actions/workflows/test.yml/badge.svg)](https://github.com/thumbor/ruby-thumbor/actions)
3
+ [![Github Actions - rubocop](https://github.com/thumbor/ruby-thumbor/actions/workflows/rubocop-analysis.yml/badge.svg)](https://github.com/thumbor/ruby-thumbor/actions)
4
+ [![Gem Version](https://badge.fury.io/rb/ruby-thumbor.svg)](https://rubygems.org/gems/ruby-thumbor)
5
+ [![Coverage Status](https://coveralls.io/repos/thumbor/ruby-thumbor/badge.svg?branch=master&service=github)](https://coveralls.io/github/thumbor/ruby-thumbor?branch=master)
6
+
7
+ ruby-thumbor is the Ruby client to the thumbor imaging service (http://github.com/thumbor/thumbor).
8
+
9
+ ## Features
10
+ * Generate thumbor encrypted URLs
11
+ * Obtain metadata from image operations in thumbor
12
+
13
+ ## Install
14
+ ``` bash
15
+ gem install ruby-thumbor
16
+ ```
17
+
18
+ ## Using it
19
+ ``` ruby
20
+ require 'ruby-thumbor'
21
+
22
+ image = Thumbor::Cascade.new('my-security-key', 'remote-image.com/path/to/image.jpg')
23
+ image.width(300).height(200).watermark_filter('http://remote-image.com/path/to/image.jpg', 30).generate
24
+
25
+ # url will contain something like:
26
+ # /2913921in321n3k2nj32hjhj3h22/remote-image.com/path/to/image.jpg
27
+
28
+ # Now you just need to concatenate this generated path, with your thumbor server url
29
+ ```
30
+
31
+ ### or
32
+ ``` ruby
33
+ require 'ruby-thumbor'
34
+
35
+ crypto = Thumbor::CryptoURL.new 'my-security-key'
36
+
37
+ url = crypto.generate(width: 200, height: 300, image: 'remote-image.com/path/to/image.jpg')
38
+ ```
39
+
40
+ ## Available Thumbor arguments
41
+ ``` ruby
42
+ meta: bool # flag that indicates that thumbor should return only meta-data on the operations it would otherwise perform;
43
+
44
+ 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);
45
+
46
+ width: <int> # the width for the thumbnail;
47
+
48
+ height: <int> # the height for the thumbnail;
49
+
50
+ flip: <bool> # flag that indicates that thumbor should flip horizontally (on the vertical axis) the image;
51
+
52
+ flop: <bool> # flag that indicates that thumbor should flip vertically (on the horizontal axis) the image;
53
+
54
+ halign: :left, :center or :right # horizontal alignment that thumbor should use for cropping;
55
+
56
+ valign: :top, :middle or :bottom # horizontal alignment that thumbor should use for cropping;
57
+
58
+ smart: <bool> # flag that indicates that thumbor should use smart cropping;
59
+
60
+ filters: ['blur(20)', 'watermark(http://my.site.com/img.png,-10,-10,50)'] # array of filters and their arguments
61
+ ```
62
+
63
+ If you need more info on what each option does, check thumbor's documentation at https://thumbor.readthedocs.io/en/latest/index.html.
64
+
65
+ ## Uploading to Rubygems
66
+ To upload a new version to Rubygems, simply update the version on the file `lib/thumbor/version.rb` then use the commit message `Bump to <new version>`, this will trigger the release process on Github actions.
67
+
68
+
69
+ ## License
70
+ (The MIT License)
71
+
72
+ Copyright (c) 2022 Bernardo Heynemann <heynemann@gmail.com>
73
+
74
+ Permission is hereby granted, free of charge, to any person obtaining
75
+ a copy of this software and associated documentation files (the
76
+ 'Software'), to deal in the Software without restriction, including
77
+ without limitation the rights to use, copy, modify, merge, publish,
78
+ distribute, sublicense, and/or sell copies of the Software, and to
79
+ permit persons to whom the Software is furnished to do so, subject to
80
+ the following conditions:
81
+
82
+ The above copyright notice and this permission notice shall be
83
+ included in all copies or substantial portions of the Software.
84
+
85
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
88
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
89
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
90
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
91
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/ruby-thumbor.rb CHANGED
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thumbor/crypto_url'
2
4
  require 'thumbor/cascade'
3
5
 
4
6
  module Thumbor
5
7
  end
6
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'openssl'
3
5
  require 'base64'
@@ -6,20 +8,23 @@ require 'cgi'
6
8
 
7
9
  module Thumbor
8
10
  class Cascade
9
- attr_accessor :image, :old_crypto, :options, :filters
11
+ attr_accessor :image, :crypto, :options, :filters
12
+
13
+ FILTER_REGEX = Regexp.new('^(.+)_filter$')
10
14
 
11
- @available_options = [
12
- :meta, :crop, :center,
13
- :original_width, :original_height,
14
- :width, :height, :flip,
15
- :flop, :halign, :valign,
16
- :smart, :fit_in, :adaptive_fit_in,
17
- :full_fit_in, :adaptive_full_fit_in,
18
- :old, :trim]
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
 
22
- def_delegators :@old_crypto, :computed_key
27
+ def_delegators :@crypto, :computed_key
23
28
 
24
29
  @available_options.each do |opt|
25
30
  define_method(opt) do |*args|
@@ -29,49 +34,48 @@ module Thumbor
29
34
  end
30
35
  end
31
36
 
32
- def initialize(key=false, image=nil)
37
+ def initialize(key = nil, image = nil)
33
38
  @key = key
34
39
  @image = image
35
40
  @options = {}
36
41
  @filters = []
37
- @old_crypto = Thumbor::CryptoURL.new @key
38
- end
39
-
40
- def url_for
41
- @old_crypto.url_for prepare_options(@options).merge({image: @image, filters: @filters})
42
+ @crypto = Thumbor::CryptoURL.new @key
42
43
  end
43
44
 
44
45
  def generate
45
- @old_crypto.generate prepare_options(@options).merge({image: @image, filters: @filters})
46
+ @crypto.generate prepare_options(@options).merge({ image: @image, filters: @filters })
46
47
  end
47
48
 
48
- def method_missing(m, *args)
49
- if /^(.+)_filter$/.match(m.to_s)
50
- @filters << "#{$1}(#{escape_args(args).join(',')})"
49
+ def method_missing(method, *args)
50
+ if method =~ FILTER_REGEX
51
+ @filters << "#{FILTER_REGEX.match(method)[1]}(#{escape_args(args).join(',')})"
51
52
  self
52
53
  else
53
54
  super
54
55
  end
55
56
  end
56
57
 
58
+ def respond_to_missing?(method, include_private = false)
59
+ method =~ FILTER_REGEX || super
60
+ end
61
+
57
62
  private
58
63
 
59
64
  def escape_args(args)
60
65
  args.map do |arg|
61
- arg = CGI::escape(arg) if arg.is_a? String and arg.match(/^https?:\/\//)
66
+ arg = CGI.escape(arg) if arg.is_a?(String) && arg.match(%r{^https?://})
62
67
  arg
63
68
  end
64
69
  end
65
70
 
66
71
  def prepare_options(options)
67
- options.reduce({}) do |final_options, item|
72
+ options.each_with_object({}) do |item, final_options|
68
73
  value = if item[1].length == 1
69
- item[1].first
70
- else
71
- item[1]
72
- end
74
+ item[1].first
75
+ else
76
+ item[1]
77
+ end
73
78
  final_options[item[0]] = value
74
- final_options
75
79
  end
76
80
  end
77
81
  end
@@ -1,236 +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
- class CryptoURL
8
- attr_accessor :computed_key
9
-
10
- def initialize(key=false)
11
- @key = key
12
- end
13
-
14
- def computed_key
15
- (@key * 16)[0..15]
16
- end
9
+ class CryptoURL
10
+ def initialize(key = nil)
11
+ @key = key
12
+ end
17
13
 
18
- def pad(s)
19
- s + ("{" * (16 - s.length % 16))
20
- end
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
21
39
 
22
- def calculate_width_and_height(url_parts, options)
23
- width = options[:width]
24
- height = options[:height]
25
-
26
- if width and options[:flip]
27
- width = width * -1
28
- end
29
- if height and options[:flop]
30
- height = height * -1
31
- end
32
-
33
- if width or height
34
- width = 0 if not width
35
- height = 0 if not height
36
- end
37
-
38
- has_width = width
39
- has_height = height
40
- if options[:flip] and not has_width and not has_height
41
- width = "-0"
42
- height = '0' if not has_height and not options[:flop]
43
- end
44
- if options[:flop] and not has_width and not has_height
45
- height = "-0"
46
- width = '0' if not has_width and not options[:flip]
47
- end
48
-
49
- url_parts.push("#{width}x#{height}") if width || height
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
50
82
  end
51
83
 
52
- def calculate_centered_crop(options)
53
- width = options[:width]
54
- height = options[:height]
55
- original_width = options[:original_width]
56
- original_height = options[:original_height]
57
- center = options[:center]
58
-
59
- return unless original_width &&
60
- original_height &&
61
- center &&
62
- (width || height)
63
-
64
- unless center.kind_of?(Array) && center.length == 2
65
- raise 'center must be an array of x,y'
66
- end
67
-
68
- center_x, center_y = center
69
- width ||= original_width
70
- height ||= original_height
71
- width = width.abs
72
- height = height.abs
73
- new_aspect_ratio = width / height.to_f
74
- original_aspect_ratio = original_width/original_height.to_f
75
-
76
- crop = nil
77
- # We're going wider, vertical crop
78
- if new_aspect_ratio > original_aspect_ratio
79
- # How tall should we be? because new_aspect_ratio is > original_aspect_ratio we can be sure
80
- # that cropped_height is always less than original_height (original). This is assumed below.
81
- cropped_height = (original_width / new_aspect_ratio).round
82
- # Calculate coordinates around center
83
- top_crop_point = (center_y - (cropped_height * 0.5)).round
84
- bottom_crop_point = (center_y + (cropped_height * 0.5)).round
85
-
86
- # If we've gone above the top of the image, take all from the bottom
87
- if top_crop_point < 0
88
- top_crop_point = 0
89
- bottom_crop_point = cropped_height
90
- # If we've gone below the top of the image, take all from the top
91
- elsif bottom_crop_point > original_height
92
- top_crop_point = original_height - cropped_height
93
- bottom_crop_point = original_height
94
- # Because cropped_height < original_height, top_crop_point and
95
- # bottom_crop_point will never both be out of bounds
96
- end
97
-
98
- # Put it together
99
- crop = [0, top_crop_point, original_width, bottom_crop_point]
100
- # We're going taller, horizontal crop
101
- elsif new_aspect_ratio < original_aspect_ratio
102
- # How wide should we be? because new_aspect_ratio is < original_aspect_ratio we can be sure
103
- # that cropped_width is always less than original_width (original). This is assumed below.
104
- cropped_width = (original_height * new_aspect_ratio).round
105
- # Calculate coordinates around center
106
- left_crop_point = (center_x - (cropped_width * 0.5)).round
107
- right_crop_point = (center_x + (cropped_width * 0.5)).round
108
-
109
- # If we've gone beyond the left of the image, take all from the right
110
- if left_crop_point < 0
111
- left_crop_point = 0
112
- right_crop_point = cropped_width
113
- # If we've gone beyond the right of the image, take all from the left
114
- elsif right_crop_point > original_width
115
- left_crop_point = original_width - cropped_width
116
- right_crop_point = original_width
117
- # Because cropped_width < original_width, left_crop_point and
118
- # right_crop_point will never both be out of bounds
119
- end
120
-
121
- # Put it together
122
- crop = [left_crop_point, 0, right_crop_point, original_height]
123
- end
124
-
125
- 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
126
105
  end
127
106
 
128
- def url_for(options, include_hash = true)
129
- if not options[:image]
130
- raise 'image is a required argument.'
131
- end
132
-
133
- url_parts = Array.new
107
+ # Put it together
108
+ crop = [left_crop_point, 0, right_crop_point, original_height]
109
+ end
134
110
 
135
- if options[:trim]
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
111
+ options[:crop] = crop
112
+ end
140
113
 
141
- if options[:meta]
142
- url_parts.push('meta')
143
- end
114
+ def url_for(options)
115
+ raise 'image is a required argument.' unless options[:image]
144
116
 
145
- calculate_centered_crop(options)
117
+ url_parts = []
146
118
 
147
- crop = options[:crop]
148
- if crop
149
- crop_left = crop[0]
150
- crop_top = crop[1]
151
- crop_right = crop[2]
152
- crop_bottom = crop[3]
119
+ url_parts.push('debug') if options[:debug]
153
120
 
154
- if crop_left > 0 or crop_top > 0 or crop_bottom > 0 or crop_right > 0
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
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
158
126
 
159
- [:fit_in, :adaptive_fit_in, :full_fit_in, :adaptive_full_fit_in].each do |fit|
160
- if options[fit]
161
- url_parts.push(fit.to_s.gsub('_','-'))
162
- end
163
- end
127
+ url_parts.push('meta') if options[:meta]
164
128
 
165
- if options.include?(:fit_in) or options.include?(:full_fit_in) and not (options.include?(:width) or options.include?(:height))
166
- raise ArgumentError, 'When using fit-in or full-fit-in, you must specify width and/or height.'
167
- end
129
+ calculate_centered_crop(options)
168
130
 
169
- calculate_width_and_height(url_parts, options)
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]
170
137
 
171
- if options[:halign] and options[:halign] != :center
172
- url_parts.push(options[:halign])
173
- end
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
174
142
 
175
- if options[:valign] and options[:valign] != :middle
176
- url_parts.push(options[:valign])
177
- end
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
178
146
 
179
- if options[:smart]
180
- url_parts.push('smart')
181
- end
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
182
150
 
183
- if options[:filters] && !options[:filters].empty?
184
- filter_parts = []
185
- options[:filters].each do |filter|
186
- filter_parts.push(filter)
187
- end
151
+ calculate_width_and_height(url_parts, options)
188
152
 
189
- url_parts.push("filters:#{ filter_parts.join(':') }")
190
- end
153
+ url_parts.push(options[:halign]) if options[:halign] && (options[:halign] != :center)
191
154
 
192
- if include_hash
193
- image_hash = Digest::MD5.hexdigest(options[:image])
194
- url_parts.push(image_hash)
195
- end
155
+ url_parts.push(options[:valign]) if options[:valign] && (options[:valign] != :middle)
196
156
 
197
- return url_parts.join('/')
198
- end
157
+ url_parts.push('smart') if options[:smart]
199
158
 
200
- def url_safe_base64(str)
201
- Base64.encode64(str).gsub('+', '-').gsub('/', '_').gsub!(/[\n]/, '')
159
+ if options[:filters] && !options[:filters].empty?
160
+ filter_parts = []
161
+ options[:filters].each do |filter|
162
+ filter_parts.push(filter)
202
163
  end
203
164
 
204
- def generate_old(options)
205
- url = pad(url_for(options))
206
- cipher = OpenSSL::Cipher::Cipher.new('aes-128-ecb').encrypt
207
- cipher.key = computed_key
208
- encrypted = cipher.update(url)
209
- based = url_safe_base64(encrypted)
165
+ url_parts.push("filters:#{filter_parts.join(':')}")
166
+ end
210
167
 
211
- "/#{based}/#{options[:image]}"
212
- end
168
+ url_parts.join('/')
169
+ end
213
170
 
214
- def generate_new(options)
215
- thumbor_path = ""
171
+ def url_safe_base64(str)
172
+ Base64.encode64(str).gsub('+', '-').gsub('/', '_').gsub!(/\n/, '')
173
+ end
216
174
 
217
- image_options = url_for(options, false)
218
- thumbor_path << image_options + '/' unless image_options.empty?
175
+ def generate(options)
176
+ thumbor_path = []
219
177
 
220
- thumbor_path << options[:image]
178
+ image_options = url_for(options)
179
+ thumbor_path << "#{image_options}/" unless image_options.empty?
221
180
 
222
- if @key
223
- signature = url_safe_base64(OpenSSL::HMAC.digest('sha1', @key, thumbor_path))
224
- thumbor_path.insert(0, "/#{signature}/")
225
- else
226
- thumbor_path.insert(0, "/unsafe/")
227
- end
228
- thumbor_path
229
- end
181
+ thumbor_path << options[:image]
230
182
 
231
- def generate(options)
232
- return generate_old(options) if options[:old]
233
- generate_new(options)
234
- end
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
235
190
  end
191
+ end
236
192
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Thumbor
2
- VERSION = '2.0.1'
4
+ VERSION = '4.0.2'
3
5
  end
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
2
- require 'coveralls'
3
- Coveralls.wear!
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'
9
+ end
10
+
11
+ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
12
+ [
13
+ SimpleCov::Formatter::HTMLFormatter,
14
+ SimpleCov::Formatter::LcovFormatter
15
+ ]
16
+ )
4
17
 
5
18
  SimpleCov.start do
6
19
  add_filter '/spec/'
7
20
  end
8
21
 
22
+ SimpleCov.at_exit do
23
+ SimpleCov.result.format!
24
+ end
9
25
 
10
26
  RSpec.configure do |c|
11
- c.filter_run :focus => true
27
+ c.filter_run focus: true
12
28
  c.run_all_when_everything_filtered = true
13
29
  end