imgproxy 1.2.0 → 2.1.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 +238 -113
  3. data/lib/imgproxy/builder.rb +47 -55
  4. data/lib/imgproxy/config.rb +96 -30
  5. data/lib/imgproxy/extensions/active_storage.rb +10 -0
  6. data/lib/imgproxy/extensions/shrine.rb +10 -0
  7. data/lib/imgproxy/options.rb +86 -120
  8. data/lib/imgproxy/options_aliases.rb +45 -0
  9. data/lib/imgproxy/options_casters/adjust.rb +22 -0
  10. data/lib/imgproxy/options_casters/array.rb +12 -0
  11. data/lib/imgproxy/options_casters/base64.rb +12 -0
  12. data/lib/imgproxy/options_casters/bool.rb +12 -0
  13. data/lib/imgproxy/options_casters/crop.rb +23 -0
  14. data/lib/imgproxy/options_casters/extend.rb +26 -0
  15. data/lib/imgproxy/options_casters/float.rb +16 -0
  16. data/lib/imgproxy/options_casters/gif_options.rb +21 -0
  17. data/lib/imgproxy/options_casters/gravity.rb +23 -0
  18. data/lib/imgproxy/options_casters/group.rb +21 -0
  19. data/lib/imgproxy/options_casters/integer.rb +10 -0
  20. data/lib/imgproxy/options_casters/jpeg_options.rb +26 -0
  21. data/lib/imgproxy/options_casters/png_options.rb +23 -0
  22. data/lib/imgproxy/options_casters/resize.rb +21 -0
  23. data/lib/imgproxy/options_casters/size.rb +24 -0
  24. data/lib/imgproxy/options_casters/string.rb +10 -0
  25. data/lib/imgproxy/options_casters/trim.rb +28 -0
  26. data/lib/imgproxy/options_casters/watermark.rb +30 -0
  27. data/lib/imgproxy/trim_array.rb +11 -0
  28. data/lib/imgproxy/url_adapters/active_storage.rb +35 -0
  29. data/lib/imgproxy/url_adapters/shrine.rb +16 -6
  30. data/lib/imgproxy/url_adapters.rb +0 -4
  31. data/lib/imgproxy/version.rb +1 -1
  32. data/lib/imgproxy.rb +72 -50
  33. metadata +69 -23
  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
@@ -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,119 @@
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
+ return_attachment: Imgproxy::OptionsCasters::Bool,
68
+ expires: Imgproxy::OptionsCasters::Integer,
69
+ }.freeze
70
+
71
+ META = %i[size resize adjust].freeze
19
72
 
20
73
  # @param options [Hash] raw processing options
21
74
  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 }])
75
+ super()
32
76
 
33
- freeze
34
- end
35
-
36
- private
77
+ # Options order hack: initialize known and meta options with nil value to preserve order
78
+ CASTERS.each_key { |n| self[n] = nil if options.key?(n) || META.include?(n) }
37
79
 
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
80
+ options.each do |name, value|
81
+ caster = CASTERS[name]
82
+ self[name] = caster ? caster.cast(value) : unwrap_hash(value)
48
83
  end
49
- end
50
84
 
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
-
59
- def group_options
60
- group_crop_opts
61
- group_extend_opts
62
85
  group_resizing_opts
63
- group_gravity_opts
64
86
  group_adjust_opts
65
- group_watermark_opts
66
- group_trim_opts
67
- end
68
87
 
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)
88
+ compact!
89
+ end
73
90
 
74
- return unless crop_width || crop_height
91
+ private
75
92
 
76
- crop_gravity = nil if crop_gravity[0].nil?
93
+ def unwrap_hash(raw)
94
+ return raw unless raw.is_a?(Hash)
77
95
 
78
- self[:crop] = [crop_width || 0, crop_height || 0, *crop_gravity]
96
+ raw.flat_map do |_key, val|
97
+ unwrap_hash(val)
98
+ end
79
99
  end
80
100
 
81
101
  def group_resizing_opts
82
- return unless self[:width] && self[:height]
102
+ return unless self[:width] && self[:height] && !self[:size] && !self[:resize]
83
103
 
84
104
  self[:size] = extract_and_trim_nils(:width, :height, :enlarge, :extend)
85
-
86
105
  self[:resize] = [delete(:resizing_type), *delete(:size)] if self[:resizing_type]
87
106
  end
88
107
 
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
108
  def group_adjust_opts
106
- return unless values_at(:brightness, :contrast, :saturation).count { |o| !o.nil? } > 1
109
+ return if self[:adjust]
110
+ return unless values_at(:brightness, :contrast, :saturation).count { |o| o } > 1
107
111
 
108
112
  self[:adjust] = extract_and_trim_nils(:brightness, :contrast, :saturation)
109
113
  end
110
114
 
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
115
  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
116
+ keys.map { |k| delete(k) }.trim!
151
117
  end
152
118
  end
153
119
  end
@@ -0,0 +1,45 @@
1
+ module Imgproxy
2
+ OPTIONS_ALIASES = {
3
+ resize: :rs,
4
+ size: :s,
5
+ resizing_type: :rt,
6
+ resizing_algorithm: :ra,
7
+ width: :w,
8
+ height: :h,
9
+ enlarge: :el,
10
+ extend: :ex,
11
+ gravity: :g,
12
+ crop: :c,
13
+ padding: :pd,
14
+ trim: :t,
15
+ rotate: :rot,
16
+ quality: :q,
17
+ max_bytes: :mb,
18
+ background: :bg,
19
+ background_alpha: :bga,
20
+ adjust: :a,
21
+ brightness: :br,
22
+ contrast: :co,
23
+ saturation: :sa,
24
+ blur: :bl,
25
+ sharpen: :sh,
26
+ pixelate: :pix,
27
+ unsharpening: :ush,
28
+ watermark: :wm,
29
+ watermark_url: :wmu,
30
+ style: :st,
31
+ jpeg_options: :jpego,
32
+ png_options: :pngo,
33
+ gif_options: :gifo,
34
+ page: :pg,
35
+ video_thumbnail_second: :vts,
36
+ preset: :pr,
37
+ cachebuster: :cb,
38
+ strip_metadata: :sm,
39
+ strip_color_profile: :scp,
40
+ auto_rotate: :ar,
41
+ filename: :fn,
42
+ return_attachment: :att,
43
+ expires: :exp,
44
+ }.freeze
45
+ end
@@ -0,0 +1,22 @@
1
+ require "imgproxy/options_casters/group"
2
+ require "imgproxy/options_casters/integer"
3
+ require "imgproxy/options_casters/float"
4
+
5
+ module Imgproxy
6
+ module OptionsCasters
7
+ # Casts gravity option
8
+ module Adjust
9
+ CASTER = Imgproxy::OptionsCasters::Group.new(
10
+ brightness: Imgproxy::OptionsCasters::Integer,
11
+ contrast: Imgproxy::OptionsCasters::Float,
12
+ saturation: Imgproxy::OptionsCasters::Float,
13
+ ).freeze
14
+
15
+ def self.cast(raw)
16
+ return raw unless raw.is_a?(Hash)
17
+
18
+ CASTER.cast(raw)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ module Imgproxy
2
+ module OptionsCasters
3
+ # Casts array option
4
+ module Array
5
+ def self.cast(raw)
6
+ return if raw.nil?
7
+
8
+ raw.is_a?(Array) ? raw : [raw]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require "base64"
2
+
3
+ module Imgproxy
4
+ module OptionsCasters
5
+ # Casts string option to base64
6
+ module Base64
7
+ def self.cast(raw)
8
+ ::Base64.urlsafe_encode64(raw.to_s).tr("=", "") unless raw.nil?
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Imgproxy
2
+ module OptionsCasters
3
+ # Casts boolean option
4
+ module Bool
5
+ def self.cast(raw)
6
+ return if raw.nil?
7
+
8
+ raw && raw != 0 && raw != "0" ? 1 : 0
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ require "imgproxy/trim_array"
2
+ require "imgproxy/options_casters/float"
3
+ require "imgproxy/options_casters/gravity"
4
+
5
+ module Imgproxy
6
+ module OptionsCasters
7
+ # Casts crop option
8
+ module Crop
9
+ using TrimArray
10
+
11
+ def self.cast(raw)
12
+ return raw unless raw.is_a?(Hash)
13
+ return unless raw[:width] || raw[:height]
14
+
15
+ [
16
+ Imgproxy::OptionsCasters::Float.cast(raw[:width]) || 0,
17
+ Imgproxy::OptionsCasters::Float.cast(raw[:height]) || 0,
18
+ Imgproxy::OptionsCasters::Gravity.cast(raw[:gravity]),
19
+ ].trim!
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ require "imgproxy/options_casters/group"
2
+ require "imgproxy/options_casters/bool"
3
+ require "imgproxy/options_casters/gravity"
4
+
5
+ module Imgproxy
6
+ module OptionsCasters
7
+ # Casts extend option
8
+ module Extend
9
+ CASTER = Imgproxy::OptionsCasters::Group.new(
10
+ extend: Imgproxy::OptionsCasters::Bool,
11
+ gravity: Imgproxy::OptionsCasters::Gravity,
12
+ ).freeze
13
+
14
+ def self.cast(raw)
15
+ # Allow extend to be just a boolean
16
+ return Imgproxy::OptionsCasters::Bool.cast(raw) if [true, false].include?(raw)
17
+
18
+ return raw unless raw.is_a?(Hash)
19
+ return if raw[:extend].nil?
20
+
21
+ values = CASTER.cast(raw)
22
+ values[0].zero? ? 0 : values
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Imgproxy
2
+ module OptionsCasters
3
+ # Casts float option
4
+ module Float
5
+ ZERO_RE = /\.0+/.freeze
6
+
7
+ def self.cast(raw)
8
+ raw&.to_f&.then do |f|
9
+ # Convert integral value to Integer so to_s won't give us trailing zero
10
+ i = f.to_i
11
+ i == f ? i : f
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require "imgproxy/options_casters/group"
2
+ require "imgproxy/options_casters/bool"
3
+
4
+ module Imgproxy
5
+ module OptionsCasters
6
+ # Casts gif_options option
7
+ module GifOptions
8
+ CASTER = Imgproxy::OptionsCasters::Group.new(
9
+ optimize_frames: Imgproxy::OptionsCasters::Bool,
10
+ optimize_transparency: Imgproxy::OptionsCasters::Bool,
11
+ ).freeze
12
+
13
+ def self.cast(raw)
14
+ return raw unless raw.is_a?(Hash)
15
+
16
+ values = CASTER.cast(raw)
17
+ values.empty? ? nil : values
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require "imgproxy/options_casters/group"
2
+ require "imgproxy/options_casters/string"
3
+ require "imgproxy/options_casters/float"
4
+
5
+ module Imgproxy
6
+ module OptionsCasters
7
+ # Casts gravity option
8
+ module Gravity
9
+ CASTER = Imgproxy::OptionsCasters::Group.new(
10
+ type: Imgproxy::OptionsCasters::String,
11
+ x_offset: Imgproxy::OptionsCasters::Float,
12
+ y_offset: Imgproxy::OptionsCasters::Float,
13
+ ).freeze
14
+
15
+ def self.cast(raw)
16
+ return raw unless raw.is_a?(Hash)
17
+ return unless raw[:type]
18
+
19
+ CASTER.cast(raw)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require "imgproxy/trim_array"
2
+
3
+ module Imgproxy
4
+ module OptionsCasters
5
+ # Casts group of options and trim nils from the end
6
+ class Group
7
+ using TrimArray
8
+
9
+ def initialize(extractors)
10
+ @extractors = extractors
11
+ end
12
+
13
+ def cast(raw)
14
+ values = @extractors.map do |key, extractor|
15
+ extractor.cast(raw[key])
16
+ end
17
+ values.trim!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module Imgproxy
2
+ module OptionsCasters
3
+ # Casts integer option
4
+ module Integer
5
+ def self.cast(raw)
6
+ raw&.to_i
7
+ end
8
+ end
9
+ end
10
+ end