imgproxy 1.0.6 → 2.0.1

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 +236 -92
  3. data/lib/imgproxy/builder.rb +54 -37
  4. data/lib/imgproxy/config.rb +96 -25
  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 +87 -72
  8. data/lib/imgproxy/options_aliases.rb +43 -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 +25 -0
  29. data/lib/imgproxy/url_adapters/shrine.rb +15 -5
  30. data/lib/imgproxy/url_adapters.rb +0 -4
  31. data/lib/imgproxy/version.rb +1 -1
  32. data/lib/imgproxy.rb +78 -38
  33. metadata +69 -24
  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 -20
@@ -1,39 +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
- def initialize
21
- self.signature_size = 32
22
- self.use_short_options = true
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*"))
23
78
  end
24
79
 
25
- # Decodes hex-encoded key and sets it to {#key}
26
- #
27
- # @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
28
99
  def hex_key=(value)
29
- self.key = value.nil? ? nil : [value].pack("H*")
100
+ warn "[DEPRECATION] #hex_key is deprecated. Please use #key instead."
101
+ self.key = value
30
102
  end
31
103
 
32
- # Decodes hex-encoded salt and sets it to {#salt}
33
- #
34
- # @param value [String] hex-encoded signature salt
104
+ # @deprecated Please use {#salt} instead
35
105
  def hex_salt=(value)
36
- self.salt = value.nil? ? nil : [value].pack("H*")
106
+ warn "[DEPRECATION] #hex_salt is deprecated. Please use #salt instead."
107
+ self.salt = value
37
108
  end
38
109
 
39
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,100 +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 gravity watermark_position style cachebuster format].freeze
5
- INT_OPTS = %i[width height quality watermark_x_offset watermark_y_offset].freeze
6
- FLOAT_OPTS = %i[dpr gravity_x gravity_y blur sharpen watermark_opacity watermark_scale].freeze
7
- BOOL_OPTS = %i[enlarge extend].freeze
8
- ARRAY_OPTS = %i[background preset].freeze
9
- ALL_OPTS = (STRING_OPTS + INT_OPTS + FLOAT_OPTS + BOOL_OPTS + ARRAY_OPTS).freeze
10
-
11
- OPTS_PRIORITY = %i[ resize size resizing_type width height dpr enlarge extend gravity quality
12
- background blur sharpen watermark preset 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
13
70
 
14
71
  # @param options [Hash] raw processing options
15
72
  def initialize(options)
16
- merge!(options.slice(*ALL_OPTS))
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) }
17
75
 
18
- typecast
76
+ options.each do |name, value|
77
+ caster = CASTERS[name]
78
+ self[name] = caster ? caster.cast(value) : unwrap_hash(value)
79
+ end
19
80
 
20
81
  group_resizing_opts
21
- group_gravity_opts
22
- group_watermark_opts
23
-
24
- encode_style
82
+ group_adjust_opts
25
83
 
26
- replace(Hash[sort_by { |k, _| OPTS_PRIORITY.index(k) || 99 }])
27
-
28
- freeze
84
+ compact!
29
85
  end
30
86
 
31
87
  private
32
88
 
33
- def typecast
34
- compact.each do |key, value|
35
- self[key] =
36
- case key
37
- when *STRING_OPTS then value.to_s
38
- when *INT_OPTS then value.to_i
39
- when *FLOAT_OPTS then value.to_f
40
- when *BOOL_OPTS then bool(value)
41
- when *ARRAY_OPTS then wrap_array(value)
42
- end
43
- end
44
- end
45
-
46
- def bool(value)
47
- value && value != 0 && value != "0" ? 1 : 0
48
- end
89
+ def unwrap_hash(raw)
90
+ return raw unless raw.is_a?(Hash)
49
91
 
50
- def wrap_array(value)
51
- value.is_a?(Array) ? value : [value]
92
+ raw.flat_map do |_key, val|
93
+ unwrap_hash(val)
94
+ end
52
95
  end
53
96
 
54
97
  def group_resizing_opts
55
- return unless self[:width] && self[:height]
56
-
57
- self[:size] = trim_nils(
58
- [delete(:width), delete(:height), delete(:enlarge), delete(:extend)],
59
- )
98
+ return unless self[:width] && self[:height] && !self[:size] && !self[:resize]
60
99
 
100
+ self[:size] = extract_and_trim_nils(:width, :height, :enlarge, :extend)
61
101
  self[:resize] = [delete(:resizing_type), *delete(:size)] if self[:resizing_type]
62
102
  end
63
103
 
64
- def group_gravity_opts
65
- gravity = trim_nils(
66
- [
67
- delete(:gravity),
68
- delete(:gravity_x),
69
- delete(:gravity_y),
70
- ],
71
- )
72
-
73
- self[:gravity] = gravity unless gravity[0].nil?
74
- end
75
-
76
- def group_watermark_opts
77
- watermark = trim_nils(
78
- [
79
- delete(:watermark_opacity),
80
- delete(:watermark_position),
81
- delete(:watermark_x_offset),
82
- delete(:watermark_y_offset),
83
- delete(:watermark_scale),
84
- ],
85
- )
86
-
87
- self[:watermark] = watermark unless watermark[0].nil?
88
- end
104
+ def group_adjust_opts
105
+ return if self[:adjust]
106
+ return unless values_at(:brightness, :contrast, :saturation).count { |o| o } > 1
89
107
 
90
- def encode_style
91
- return if self[:style].nil?
92
- self[:style] = Base64.urlsafe_encode64(self[:style]).tr("=", "")
108
+ self[:adjust] = extract_and_trim_nils(:brightness, :contrast, :saturation)
93
109
  end
94
110
 
95
- def trim_nils(value)
96
- value.delete_at(-1) while !value.empty? && value[-1].nil?
97
- value
111
+ def extract_and_trim_nils(*keys)
112
+ keys.map { |k| delete(k) }.trim!
98
113
  end
99
114
  end
100
115
  end
@@ -0,0 +1,43 @@
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
+ }.freeze
43
+ 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