imgproxy 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -112
  3. data/lib/imgproxy.rb +70 -50
  4. data/lib/imgproxy/builder.rb +47 -55
  5. data/lib/imgproxy/config.rb +96 -30
  6. data/lib/imgproxy/extensions/active_storage.rb +10 -0
  7. data/lib/imgproxy/extensions/shrine.rb +10 -0
  8. data/lib/imgproxy/options.rb +83 -121
  9. data/lib/imgproxy/options_aliases.rb +43 -0
  10. data/lib/imgproxy/options_casters/adjust.rb +22 -0
  11. data/lib/imgproxy/options_casters/array.rb +12 -0
  12. data/lib/imgproxy/options_casters/base64.rb +12 -0
  13. data/lib/imgproxy/options_casters/bool.rb +12 -0
  14. data/lib/imgproxy/options_casters/crop.rb +23 -0
  15. data/lib/imgproxy/options_casters/extend.rb +26 -0
  16. data/lib/imgproxy/options_casters/float.rb +16 -0
  17. data/lib/imgproxy/options_casters/gif_options.rb +21 -0
  18. data/lib/imgproxy/options_casters/gravity.rb +23 -0
  19. data/lib/imgproxy/options_casters/group.rb +21 -0
  20. data/lib/imgproxy/options_casters/integer.rb +10 -0
  21. data/lib/imgproxy/options_casters/jpeg_options.rb +26 -0
  22. data/lib/imgproxy/options_casters/png_options.rb +23 -0
  23. data/lib/imgproxy/options_casters/resize.rb +21 -0
  24. data/lib/imgproxy/options_casters/size.rb +24 -0
  25. data/lib/imgproxy/options_casters/string.rb +10 -0
  26. data/lib/imgproxy/options_casters/trim.rb +28 -0
  27. data/lib/imgproxy/options_casters/watermark.rb +30 -0
  28. data/lib/imgproxy/trim_array.rb +11 -0
  29. data/lib/imgproxy/url_adapters.rb +0 -4
  30. data/lib/imgproxy/url_adapters/active_storage.rb +25 -0
  31. data/lib/imgproxy/url_adapters/shrine.rb +15 -5
  32. data/lib/imgproxy/version.rb +1 -1
  33. metadata +54 -9
  34. data/lib/imgproxy/url_adapters/active_storage_gcs.rb +0 -31
  35. data/lib/imgproxy/url_adapters/active_storage_s3.rb +0 -23
  36. data/lib/imgproxy/url_adapters/shrine_s3.rb +0 -21
@@ -3,6 +3,7 @@ require "base64"
3
3
  require "erb"
4
4
 
5
5
  require "imgproxy/options"
6
+ require "imgproxy/options_aliases"
6
7
 
7
8
  module Imgproxy
8
9
  # Builds imgproxy URL
@@ -17,19 +18,15 @@ module Imgproxy
17
18
  # builder.url_for("http://images.example.com/images/image1.jpg")
18
19
  # builder.url_for("http://images.example.com/images/image2.jpg")
19
20
  class Builder
20
- OMITTED_OPTIONS = %i[format].freeze
21
21
  # @param [Hash] options Processing options
22
22
  # @see Imgproxy.url_for
23
23
  def initialize(options = {})
24
24
  options = options.dup
25
25
 
26
- @base64_encode_url = options.delete(:base64_encode_url)
27
- @use_short_options = options.delete(:use_short_options)
28
-
29
- @use_short_options = config.use_short_options if @use_short_options.nil?
30
- @base64_encode_url = config.base64_encode_urls if @base64_encode_url.nil?
26
+ extract_builder_options(options)
31
27
 
32
28
  @options = Imgproxy::Options.new(options)
29
+ @format = @options.delete(:format)
33
30
  end
34
31
 
35
32
  # Genrates imgproxy URL
@@ -39,77 +36,68 @@ module Imgproxy
39
36
  # the configured URL adapters
40
37
  # @see Imgproxy.url_for
41
38
  def url_for(image)
42
- path = [*processing_options, url(image)].join("/")
39
+ path = [*processing_options, url(image, ext: @format)].join("/")
43
40
  signature = sign_path(path)
44
41
 
45
42
  File.join(Imgproxy.config.endpoint.to_s, signature, path)
46
43
  end
47
44
 
48
- private
45
+ # Genrates imgproxy info URL
46
+ #
47
+ # @return [String] imgproxy info URL
48
+ # @param [String,URI, Object] image Source image URL or object applicable for
49
+ # the configured URL adapters
50
+ # @see Imgproxy.info_url_for
51
+ def info_url_for(image)
52
+ path = url(image)
53
+ signature = sign_path(path)
49
54
 
50
- OPTIONS_ALIASES = {
51
- resize: :rs,
52
- size: :s,
53
- resizing_type: :rt,
54
- width: :w,
55
- height: :h,
56
- enlarge: :en,
57
- extend: :ex,
58
- gravity: :g,
59
- crop: :c,
60
- padding: :pd,
61
- trim: :t,
62
- quality: :q,
63
- max_bytes: :mb,
64
- background: :bg,
65
- adjust: :a,
66
- brightness: :br,
67
- contrast: :co,
68
- saturation: :sa,
69
- blur: :bl,
70
- sharpen: :sh,
71
- pixelate: :pix,
72
- watermark: :wm,
73
- watermark_url: :wmu,
74
- preset: :pr,
75
- cachebuster: :cb,
76
- }.freeze
55
+ File.join(Imgproxy.config.endpoint.to_s, "info", signature, path)
56
+ end
57
+
58
+ private
77
59
 
78
60
  NEED_ESCAPE_RE = /[@?% ]|[^\p{Ascii}]/.freeze
79
61
 
62
+ def extract_builder_options(options)
63
+ @use_short_options = not_nil_or(options.delete(:use_short_options), config.use_short_options)
64
+ @base64_encode_url = not_nil_or(options.delete(:base64_encode_url), config.base64_encode_urls)
65
+ @escape_plain_url =
66
+ not_nil_or(options.delete(:escape_plain_url), config.always_escape_plain_urls)
67
+ end
68
+
80
69
  def processing_options
81
- @processing_options ||=
82
- @options.reject { |k, _| OMITTED_OPTIONS.include?(k) }.map do |key, value|
83
- "#{option_alias(key)}:#{wrap_array(value).join(':')}"
84
- end
70
+ @processing_options ||= @options.map do |key, value|
71
+ [option_alias(key), value].join(":")
72
+ end
85
73
  end
86
74
 
87
- def plain_url_for(url)
88
- escaped_url = url.match?(NEED_ESCAPE_RE) ? ERB::Util.url_encode(url) : url
75
+ def url(image, ext: nil)
76
+ url = config.url_adapters.url_of(image)
89
77
 
90
- @options[:format] ? "plain/#{escaped_url}@#{@options[:format]}" : "plain/#{escaped_url}"
78
+ @base64_encode_url ? base64_url_for(url, ext: ext) : plain_url_for(url, ext: ext)
91
79
  end
92
80
 
93
- def base64_url_for(url)
94
- encoded_url = Base64.urlsafe_encode64(url).tr("=", "").scan(/.{1,16}/).join("/")
81
+ def plain_url_for(url, ext: nil)
82
+ escaped_url = need_escape_url?(url) ? ERB::Util.url_encode(url) : url
95
83
 
96
- @options[:format] ? "#{encoded_url}.#{@options[:format]}" : encoded_url
84
+ ext ? "plain/#{escaped_url}@#{ext}" : "plain/#{escaped_url}"
97
85
  end
98
86
 
99
- def option_alias(name)
100
- return name unless config.use_short_options
87
+ def base64_url_for(url, ext: nil)
88
+ encoded_url = Base64.urlsafe_encode64(url).tr("=", "").scan(/.{1,16}/).join("/")
101
89
 
102
- OPTIONS_ALIASES.fetch(name, name)
90
+ ext ? "#{encoded_url}.#{ext}" : encoded_url
103
91
  end
104
92
 
105
- def wrap_array(value)
106
- value.is_a?(Array) ? value : [value]
93
+ def need_escape_url?(url)
94
+ @escape_plain_url || url.match?(NEED_ESCAPE_RE)
107
95
  end
108
96
 
109
- def url(image)
110
- url = config.url_adapters.url_of(image)
97
+ def option_alias(name)
98
+ return name unless @use_short_options
111
99
 
112
- @base64_encode_url ? base64_url_for(url) : plain_url_for(url)
100
+ Imgproxy::OPTIONS_ALIASES.fetch(name, name)
113
101
  end
114
102
 
115
103
  def sign_path(path)
@@ -130,11 +118,11 @@ module Imgproxy
130
118
  end
131
119
 
132
120
  def signature_key
133
- config.key
121
+ config.raw_key
134
122
  end
135
123
 
136
124
  def signature_salt
137
- config.salt
125
+ config.raw_salt
138
126
  end
139
127
 
140
128
  def signature_size
@@ -144,5 +132,9 @@ module Imgproxy
144
132
  def config
145
133
  Imgproxy.config
146
134
  end
135
+
136
+ def not_nil_or(value, fallback)
137
+ value.nil? ? fallback : value
138
+ end
147
139
  end
148
140
  end
@@ -1,44 +1,110 @@
1
+ require "anyway_config"
2
+
1
3
  require "imgproxy/url_adapters"
2
4
 
3
5
  module Imgproxy
4
6
  # Imgproxy config
7
+ #
8
+ # @!attribute endpoint
9
+ # imgproxy endpoint
10
+ # @return [String]
11
+ # @!attribute key
12
+ # imgproxy hex-encoded signature key
13
+ # @return [String]
14
+ # @!attribute salt
15
+ # imgproxy hex-encoded signature salt
16
+ # @return [String]
17
+ # @!attribute raw_key
18
+ # Decoded signature key
19
+ # @return [String]
20
+ # @!attribute raw_salt
21
+ # Decoded signature salt
22
+ # @return [String]
23
+ # @!attribute signature_size
24
+ # imgproxy signature size. Defaults to 32
25
+ # @return [String]
26
+ # @!attribute use_short_options
27
+ # Use short processing option names (+rs+ for +resize+, +g+ for +gravity+, etc).
28
+ # Defaults to true
29
+ # @return [String]
30
+ # @!attribute base64_encode_urls
31
+ # Base64 encode the URL. Defaults to false
32
+ # @return [String]
33
+ # @!attribute always_escape_plain_urls
34
+ # Always escape plain URLs. Defaults to false
35
+ # @return [String]
36
+ # @!attribute use_s3_urls
37
+ # Use short S3 urls (s3://...) when possible. Defaults to false
38
+ # @return [String]
39
+ # @!attribute use_gcs_urls
40
+ # Use short Google Cloud Storage urls (gs://...) when possible. Defaults to false
41
+ # @return [String]
42
+ # @!attribute gcs_bucket
43
+ # Google Cloud Storage bucket name
44
+ # @return [String]
45
+ # @!attribute shrine_host
46
+ # Shrine host
47
+ # @return [String]
48
+ #
5
49
  # @see Imgproxy.configure
6
- class Config
7
- # @return [String] imgproxy endpoint
8
- attr_accessor :endpoint
9
- # @return [String] imgproxy signature key
10
- attr_accessor :key
11
- # @return [String] imgproxy signature salt
12
- attr_accessor :salt
13
- # @return [Integer] imgproxy signature size. Defaults to 32
14
- attr_accessor :signature_size
15
- # @return [Boolean] use short processing option names
16
- # (`rs` for `resize`, `g` for `gravity`, etc).
17
- # Defaults to true
18
- attr_accessor :use_short_options
19
-
20
- # @return [Boolean] base64 encode the URL
21
- # Defaults to false
22
- attr_accessor :base64_encode_urls
23
-
24
- def initialize
25
- self.signature_size = 32
26
- self.use_short_options = true
27
- self.base64_encode_urls = false
50
+ # @see https://github.com/palkan/anyway_config anyway_config
51
+ class Config < Anyway::Config
52
+ attr_config(
53
+ :endpoint,
54
+ :key,
55
+ :salt,
56
+ :raw_key,
57
+ :raw_salt,
58
+ signature_size: 32,
59
+ use_short_options: true,
60
+ base64_encode_urls: false,
61
+ always_escape_plain_urls: false,
62
+ use_s3_urls: false,
63
+ use_gcs_urls: false,
64
+ gcs_bucket: nil,
65
+ shrine_host: nil,
66
+ )
67
+
68
+ alias_method :set_key, :key=
69
+ alias_method :set_raw_key, :raw_key=
70
+ alias_method :set_salt, :salt=
71
+ alias_method :set_raw_salt, :raw_salt=
72
+ private :set_key, :set_raw_key, :set_salt, :set_raw_salt
73
+
74
+ def key=(value)
75
+ value = value&.to_s
76
+ super(value)
77
+ set_raw_key(value && [value].pack("H*"))
28
78
  end
29
79
 
30
- # Decodes hex-encoded key and sets it to {#key}
31
- #
32
- # @param value [String] hex-encoded signature key
80
+ def raw_key=(value)
81
+ value = value&.to_s
82
+ super(value)
83
+ set_key(value&.unpack("H*")&.first)
84
+ end
85
+
86
+ def salt=(value)
87
+ value = value&.to_s
88
+ super(value)
89
+ set_raw_salt(value && [value].pack("H*"))
90
+ end
91
+
92
+ def raw_salt=(value)
93
+ value = value&.to_s
94
+ super(value)
95
+ set_salt(value&.unpack("H*")&.first)
96
+ end
97
+
98
+ # @deprecated Please use {#key} instead
33
99
  def hex_key=(value)
34
- self.key = value.nil? ? nil : [value].pack("H*")
100
+ warn "[DEPRECATION] #hex_key is deprecated. Please use #key instead."
101
+ self.key = value
35
102
  end
36
103
 
37
- # Decodes hex-encoded salt and sets it to {#salt}
38
- #
39
- # @param value [String] hex-encoded signature salt
104
+ # @deprecated Please use {#salt} instead
40
105
  def hex_salt=(value)
41
- self.salt = value.nil? ? nil : [value].pack("H*")
106
+ warn "[DEPRECATION] #hex_salt is deprecated. Please use #salt instead."
107
+ self.salt = value
42
108
  end
43
109
 
44
110
  # URL adapters config. Allows to use this gem with ActiveStorage, Shrine, etc.
@@ -12,6 +12,16 @@ module Imgproxy
12
12
  return options.url_for(self) if options.is_a?(Imgproxy::Builder)
13
13
  Imgproxy.url_for(self, options)
14
14
  end
15
+
16
+ # Returns imgproxy info URL for an attachment
17
+ #
18
+ # @return [String]
19
+ # @param options [Hash, Imgproxy::Builder]
20
+ # @see Imgproxy.info_url_for
21
+ def imgproxy_info_url(options = {})
22
+ return options.info_url_for(self) if options.is_a?(Imgproxy::Builder)
23
+ Imgproxy.info_url_for(self, options)
24
+ end
15
25
  end
16
26
  end
17
27
  end
@@ -12,6 +12,16 @@ module Imgproxy
12
12
  return options.url_for(self) if options.is_a?(Imgproxy::Builder)
13
13
  Imgproxy.url_for(self, options)
14
14
  end
15
+
16
+ # Returns imgproxy info URL for a Shrine::UploadedFile instance
17
+ #
18
+ # @return [String]
19
+ # @param options [Hash, Imgproxy::Builder]
20
+ # @see Imgproxy.info_url_for
21
+ def imgproxy_info_url(options = {})
22
+ return options.info_url_for(self) if options.is_a?(Imgproxy::Builder)
23
+ Imgproxy.info_url_for(self, options)
24
+ end
15
25
  end
16
26
  end
17
27
  end
@@ -1,153 +1,115 @@
1
+ require "imgproxy/trim_array"
2
+ require "imgproxy/options_casters/string"
3
+ require "imgproxy/options_casters/integer"
4
+ require "imgproxy/options_casters/float"
5
+ require "imgproxy/options_casters/bool"
6
+ require "imgproxy/options_casters/array"
7
+ require "imgproxy/options_casters/base64"
8
+ require "imgproxy/options_casters/resize"
9
+ require "imgproxy/options_casters/size"
10
+ require "imgproxy/options_casters/extend"
11
+ require "imgproxy/options_casters/gravity"
12
+ require "imgproxy/options_casters/crop"
13
+ require "imgproxy/options_casters/trim"
14
+ require "imgproxy/options_casters/adjust"
15
+ require "imgproxy/options_casters/watermark"
16
+ require "imgproxy/options_casters/jpeg_options"
17
+ require "imgproxy/options_casters/png_options"
18
+ require "imgproxy/options_casters/gif_options"
19
+
1
20
  module Imgproxy
2
21
  # Formats and regroups processing options
3
22
  class Options < Hash
4
- STRING_OPTS = %i[resizing_type extend_gravity gravity crop_gravity trim_color watermark_position
5
- watermark_url style cachebuster format base64_encode_url].freeze
6
- INT_OPTS = %i[width height crop_width crop_height trim_threshold quality brightness pixelate
7
- watermark_x_offset watermark_y_offset max_bytes].freeze
8
- FLOAT_OPTS = %i[dpr extend_gravity_x extend_gravity_y gravity_x gravity_y crop_gravity_x
9
- crop_gravity_y contrast saturation blur sharpen watermark_opacity
10
- watermark_scale].freeze
11
- BOOL_OPTS = %i[enlarge extend trim_equal_hor trim_equal_ver].freeze
12
- ARRAY_OPTS = %i[padding background preset].freeze
13
- ALL_OPTS = (STRING_OPTS + INT_OPTS + FLOAT_OPTS + BOOL_OPTS + ARRAY_OPTS).freeze
14
-
15
- OPTS_PRIORITY = %i[ resize size resizing_type width height dpr enlarge extend gravity
16
- crop padding trim quality max_bytes background adjust brightness contrast
17
- saturation blur sharpen pixelate watermark watermark_url style preset
18
- cachebuster ].freeze
23
+ using TrimArray
24
+
25
+ CASTERS = {
26
+ resize: Imgproxy::OptionsCasters::Resize,
27
+ size: Imgproxy::OptionsCasters::Size,
28
+ resizing_type: Imgproxy::OptionsCasters::String,
29
+ resizing_algorithm: Imgproxy::OptionsCasters::String,
30
+ width: Imgproxy::OptionsCasters::Integer,
31
+ height: Imgproxy::OptionsCasters::Integer,
32
+ dpr: Imgproxy::OptionsCasters::Float,
33
+ enlarge: Imgproxy::OptionsCasters::Bool,
34
+ extend: Imgproxy::OptionsCasters::Extend,
35
+ gravity: Imgproxy::OptionsCasters::Gravity,
36
+ crop: Imgproxy::OptionsCasters::Crop,
37
+ padding: Imgproxy::OptionsCasters::Array,
38
+ trim: Imgproxy::OptionsCasters::Trim,
39
+ rotate: Imgproxy::OptionsCasters::Integer,
40
+ quality: Imgproxy::OptionsCasters::Integer,
41
+ max_bytes: Imgproxy::OptionsCasters::Integer,
42
+ background: Imgproxy::OptionsCasters::Array,
43
+ background_alpha: Imgproxy::OptionsCasters::Float,
44
+ adjust: Imgproxy::OptionsCasters::Adjust,
45
+ brightness: Imgproxy::OptionsCasters::Integer,
46
+ contrast: Imgproxy::OptionsCasters::Float,
47
+ saturation: Imgproxy::OptionsCasters::Float,
48
+ blur: Imgproxy::OptionsCasters::Float,
49
+ sharpen: Imgproxy::OptionsCasters::Float,
50
+ pixelate: Imgproxy::OptionsCasters::Integer,
51
+ unsharpening: Imgproxy::OptionsCasters::String,
52
+ watermark: Imgproxy::OptionsCasters::Watermark,
53
+ watermark_url: Imgproxy::OptionsCasters::Base64,
54
+ style: Imgproxy::OptionsCasters::Base64,
55
+ jpeg_options: Imgproxy::OptionsCasters::JpegOptions,
56
+ png_options: Imgproxy::OptionsCasters::PngOptions,
57
+ gif_options: Imgproxy::OptionsCasters::GifOptions,
58
+ page: Imgproxy::OptionsCasters::Integer,
59
+ video_thumbnail_second: Imgproxy::OptionsCasters::Integer,
60
+ preset: Imgproxy::OptionsCasters::Array,
61
+ cachebuster: Imgproxy::OptionsCasters::String,
62
+ strip_metadata: Imgproxy::OptionsCasters::Bool,
63
+ strip_color_profile: Imgproxy::OptionsCasters::Bool,
64
+ auto_rotate: Imgproxy::OptionsCasters::Bool,
65
+ filename: Imgproxy::OptionsCasters::String,
66
+ format: Imgproxy::OptionsCasters::String,
67
+ }.freeze
68
+
69
+ META = %i[size resize adjust].freeze
19
70
 
20
71
  # @param options [Hash] raw processing options
21
72
  def initialize(options)
22
- merge!(options.slice(*ALL_OPTS))
23
-
24
- typecast
25
-
26
- group_options
27
-
28
- encode_style
29
- encode_watermark_url
30
-
31
- replace(Hash[sort_by { |k, _| OPTS_PRIORITY.index(k) || 99 }])
32
-
33
- freeze
34
- end
35
-
36
- private
73
+ # Options order hack: initialize known and meta options with nil value to preserve order
74
+ CASTERS.each_key { |n| self[n] = nil if options.key?(n) || META.include?(n) }
37
75
 
38
- def typecast
39
- compact.each do |key, value|
40
- self[key] =
41
- case key
42
- when *STRING_OPTS then value.to_s
43
- when *INT_OPTS then value.to_i
44
- when *FLOAT_OPTS then value.to_f
45
- when *BOOL_OPTS then bool(value)
46
- when *ARRAY_OPTS then wrap_array(value)
47
- end
76
+ options.each do |name, value|
77
+ caster = CASTERS[name]
78
+ self[name] = caster ? caster.cast(value) : unwrap_hash(value)
48
79
  end
49
- end
50
-
51
- def bool(value)
52
- value && value != 0 && value != "0" ? 1 : 0
53
- end
54
-
55
- def wrap_array(value)
56
- value.is_a?(Array) ? value : [value]
57
- end
58
80
 
59
- def group_options
60
- group_crop_opts
61
- group_extend_opts
62
81
  group_resizing_opts
63
- group_gravity_opts
64
82
  group_adjust_opts
65
- group_watermark_opts
66
- group_trim_opts
67
- end
68
83
 
69
- def group_crop_opts
70
- crop_width = delete(:crop_width)
71
- crop_height = delete(:crop_height)
72
- crop_gravity = extract_and_trim_nils(:crop_gravity, :crop_gravity_x, :crop_gravity_y)
84
+ compact!
85
+ end
73
86
 
74
- return unless crop_width || crop_height
87
+ private
75
88
 
76
- crop_gravity = nil if crop_gravity[0].nil?
89
+ def unwrap_hash(raw)
90
+ return raw unless raw.is_a?(Hash)
77
91
 
78
- self[:crop] = [crop_width || 0, crop_height || 0, *crop_gravity]
92
+ raw.flat_map do |_key, val|
93
+ unwrap_hash(val)
94
+ end
79
95
  end
80
96
 
81
97
  def group_resizing_opts
82
- return unless self[:width] && self[:height]
98
+ return unless self[:width] && self[:height] && !self[:size] && !self[:resize]
83
99
 
84
100
  self[:size] = extract_and_trim_nils(:width, :height, :enlarge, :extend)
85
-
86
101
  self[:resize] = [delete(:resizing_type), *delete(:size)] if self[:resizing_type]
87
102
  end
88
103
 
89
- def group_gravity_opts
90
- gravity = extract_and_trim_nils(:gravity, :gravity_x, :gravity_y)
91
-
92
- self[:gravity] = gravity unless gravity[0].nil?
93
- end
94
-
95
- def group_extend_opts
96
- extend_opts =
97
- extract_and_trim_nils(:extend, :extend_gravity, :extend_gravity_x, :extend_gravity_y)
98
-
99
- return if extend_opts[0].nil?
100
- return self[:extend] = 0 if extend_opts[0].zero?
101
-
102
- self[:extend] = extend_opts
103
- end
104
-
105
104
  def group_adjust_opts
106
- return unless values_at(:brightness, :contrast, :saturation).count { |o| !o.nil? } > 1
105
+ return if self[:adjust]
106
+ return unless values_at(:brightness, :contrast, :saturation).count { |o| o } > 1
107
107
 
108
108
  self[:adjust] = extract_and_trim_nils(:brightness, :contrast, :saturation)
109
109
  end
110
110
 
111
- def group_watermark_opts
112
- watermark = extract_and_trim_nils(
113
- :watermark_opacity,
114
- :watermark_position,
115
- :watermark_x_offset,
116
- :watermark_y_offset,
117
- :watermark_scale,
118
- )
119
-
120
- self[:watermark] = watermark unless watermark[0].nil?
121
- end
122
-
123
- def group_trim_opts
124
- trim = extract_and_trim_nils(
125
- :trim_threshold,
126
- :trim_color,
127
- :trim_equal_hor,
128
- :trim_equal_ver,
129
- )
130
-
131
- self[:trim] = trim unless trim[0].nil?
132
- end
133
-
134
- def encode_style
135
- return if self[:style].nil?
136
- self[:style] = Base64.urlsafe_encode64(self[:style]).tr("=", "")
137
- end
138
-
139
- def encode_watermark_url
140
- return if self[:watermark_url].nil?
141
- self[:watermark_url] = Base64.urlsafe_encode64(self[:watermark_url]).tr("=", "")
142
- end
143
-
144
111
  def extract_and_trim_nils(*keys)
145
- trim_nils(keys.map { |k| delete(k) })
146
- end
147
-
148
- def trim_nils(value)
149
- value.delete_at(-1) while !value.empty? && value[-1].nil?
150
- value
112
+ keys.map { |k| delete(k) }.trim!
151
113
  end
152
114
  end
153
115
  end