imgproxy 1.2.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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