activestorage 5.2.8.1 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activestorage might be problematic. Click here for more details.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -157
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +6 -5
  5. data/app/assets/javascripts/activestorage.js +4 -1
  6. data/app/controllers/active_storage/base_controller.rb +3 -5
  7. data/app/controllers/active_storage/blobs_controller.rb +1 -1
  8. data/app/controllers/active_storage/disk_controller.rb +4 -1
  9. data/app/controllers/active_storage/representations_controller.rb +1 -1
  10. data/app/controllers/concerns/active_storage/set_current.rb +15 -0
  11. data/app/javascript/activestorage/blob_record.js +6 -1
  12. data/app/jobs/active_storage/analyze_job.rb +4 -0
  13. data/app/jobs/active_storage/base_job.rb +0 -1
  14. data/app/jobs/active_storage/purge_job.rb +3 -0
  15. data/app/models/active_storage/attachment.rb +18 -9
  16. data/app/models/active_storage/blob/representable.rb +5 -5
  17. data/app/models/active_storage/blob.rb +63 -22
  18. data/app/models/active_storage/filename.rb +0 -6
  19. data/app/models/active_storage/preview.rb +3 -3
  20. data/app/models/active_storage/variant.rb +51 -52
  21. data/app/models/active_storage/variation.rb +23 -92
  22. data/config/routes.rb +13 -12
  23. data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +7 -0
  24. data/lib/active_storage/analyzer/video_analyzer.rb +2 -4
  25. data/lib/active_storage/analyzer.rb +9 -4
  26. data/lib/active_storage/attached/changes/create_many.rb +46 -0
  27. data/lib/active_storage/attached/changes/create_one.rb +68 -0
  28. data/lib/active_storage/attached/changes/create_one_of_many.rb +10 -0
  29. data/lib/active_storage/attached/changes/delete_many.rb +23 -0
  30. data/lib/active_storage/attached/changes/delete_one.rb +19 -0
  31. data/lib/active_storage/attached/changes.rb +16 -0
  32. data/lib/active_storage/attached/many.rb +16 -10
  33. data/lib/active_storage/attached/model.rb +140 -0
  34. data/lib/active_storage/attached/one.rb +16 -19
  35. data/lib/active_storage/attached.rb +7 -22
  36. data/lib/active_storage/downloader.rb +44 -0
  37. data/lib/active_storage/downloading.rb +8 -0
  38. data/lib/active_storage/engine.rb +36 -21
  39. data/lib/active_storage/errors.rb +22 -3
  40. data/lib/active_storage/gem_version.rb +4 -4
  41. data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
  42. data/lib/active_storage/previewer/video_previewer.rb +2 -3
  43. data/lib/active_storage/previewer.rb +21 -11
  44. data/lib/active_storage/reflection.rb +64 -0
  45. data/lib/active_storage/service/azure_storage_service.rb +30 -14
  46. data/lib/active_storage/service/configurator.rb +3 -1
  47. data/lib/active_storage/service/disk_service.rb +20 -16
  48. data/lib/active_storage/service/gcs_service.rb +48 -46
  49. data/lib/active_storage/service/mirror_service.rb +1 -1
  50. data/lib/active_storage/service/s3_service.rb +10 -9
  51. data/lib/active_storage/service.rb +5 -6
  52. data/lib/active_storage/transformers/image_processing_transformer.rb +39 -0
  53. data/lib/active_storage/transformers/mini_magick_transformer.rb +38 -0
  54. data/lib/active_storage/transformers/transformer.rb +42 -0
  55. data/lib/active_storage.rb +13 -292
  56. data/lib/tasks/activestorage.rake +7 -0
  57. metadata +31 -19
  58. data/app/models/active_storage/filename/parameters.rb +0 -36
  59. data/lib/active_storage/attached/macros.rb +0 -110
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "image_processing"
4
+
5
+ module ActiveStorage
6
+ module Transformers
7
+ class ImageProcessingTransformer < Transformer
8
+ private
9
+ def process(file, format:)
10
+ processor.
11
+ source(file).
12
+ loader(page: 0).
13
+ convert(format).
14
+ apply(operations).
15
+ call
16
+ end
17
+
18
+ def processor
19
+ ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize)
20
+ end
21
+
22
+ def operations
23
+ transformations.each_with_object([]) do |(name, argument), list|
24
+ if name.to_s == "combine_options"
25
+ ActiveSupport::Deprecation.warn <<~WARNING
26
+ Active Storage's ImageProcessing transformer doesn't support :combine_options,
27
+ as it always generates a single ImageMagick command. Passing :combine_options will
28
+ not be supported in Rails 6.1.
29
+ WARNING
30
+
31
+ list.concat argument.keep_if { |key, value| value.present? }.to_a
32
+ elsif argument.present?
33
+ list << [ name, argument ]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_magick"
4
+
5
+ module ActiveStorage
6
+ module Transformers
7
+ class MiniMagickTransformer < Transformer
8
+ private
9
+ def process(file, format:)
10
+ image = MiniMagick::Image.new(file.path, file)
11
+
12
+ transformations.each do |name, argument_or_subtransformations|
13
+ image.mogrify do |command|
14
+ if name.to_s == "combine_options"
15
+ argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
16
+ pass_transform_argument(command, subtransformation_name, subtransformation_argument)
17
+ end
18
+ else
19
+ pass_transform_argument(command, name, argument_or_subtransformations)
20
+ end
21
+ end
22
+ end
23
+
24
+ image.format(format) if format
25
+
26
+ image.tempfile.tap(&:open)
27
+ end
28
+
29
+ def pass_transform_argument(command, method, argument)
30
+ if argument == true
31
+ command.public_send(method)
32
+ elsif argument.present?
33
+ command.public_send(method, argument)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveStorage
4
+ module Transformers
5
+ # A Transformer applies a set of transformations to an image.
6
+ #
7
+ # The following concrete subclasses are included in Active Storage:
8
+ #
9
+ # * ActiveStorage::Transformers::ImageProcessingTransformer:
10
+ # backed by ImageProcessing, a common interface for MiniMagick and ruby-vips
11
+ #
12
+ # * ActiveStorage::Transformers::MiniMagickTransformer:
13
+ # backed by MiniMagick, a wrapper around the ImageMagick CLI
14
+ class Transformer
15
+ attr_reader :transformations
16
+
17
+ def initialize(transformations)
18
+ @transformations = transformations
19
+ end
20
+
21
+ # Applies the transformations to the source image in +file+, producing a target image in the
22
+ # specified +format+. Yields an open Tempfile containing the target image. Closes and unlinks
23
+ # the output tempfile after yielding to the given block. Returns the result of the block.
24
+ def transform(file, format:)
25
+ output = process(file, format: format)
26
+
27
+ begin
28
+ yield output
29
+ ensure
30
+ output.close!
31
+ end
32
+ end
33
+
34
+ private
35
+ # Returns an open Tempfile containing a transformed image in the given +format+.
36
+ # All subclasses implement this method.
37
+ def process(file, format:) #:doc:
38
+ raise NotImplementedError
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2017-2018 David Heinemeier Hansson, Basecamp
4
+ # Copyright (c) 2017-2019 David Heinemeier Hansson, Basecamp
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -42,302 +42,23 @@ module ActiveStorage
42
42
 
43
43
  mattr_accessor :logger
44
44
  mattr_accessor :verifier
45
- mattr_accessor :queue
45
+ mattr_accessor :queues, default: {}
46
46
  mattr_accessor :previewers, default: []
47
47
  mattr_accessor :analyzers, default: []
48
+ mattr_accessor :variant_processor, default: :mini_magick
48
49
  mattr_accessor :paths, default: {}
49
50
  mattr_accessor :variable_content_types, default: []
50
51
  mattr_accessor :content_types_to_serve_as_binary, default: []
51
52
  mattr_accessor :content_types_allowed_inline, default: []
52
53
  mattr_accessor :binary_content_type, default: "application/octet-stream"
53
- mattr_accessor :supported_image_processing_methods, default: [
54
- "adaptive_blur",
55
- "adaptive_resize",
56
- "adaptive_sharpen",
57
- "adjoin",
58
- "affine",
59
- "alpha",
60
- "annotate",
61
- "antialias",
62
- "append",
63
- "apply",
64
- "attenuate",
65
- "authenticate",
66
- "auto_gamma",
67
- "auto_level",
68
- "auto_orient",
69
- "auto_threshold",
70
- "backdrop",
71
- "background",
72
- "bench",
73
- "bias",
74
- "bilateral_blur",
75
- "black_point_compensation",
76
- "black_threshold",
77
- "blend",
78
- "blue_primary",
79
- "blue_shift",
80
- "blur",
81
- "border",
82
- "bordercolor",
83
- "borderwidth",
84
- "brightness_contrast",
85
- "cache",
86
- "canny",
87
- "caption",
88
- "channel",
89
- "channel_fx",
90
- "charcoal",
91
- "chop",
92
- "clahe",
93
- "clamp",
94
- "clip",
95
- "clip_path",
96
- "clone",
97
- "clut",
98
- "coalesce",
99
- "colorize",
100
- "colormap",
101
- "color_matrix",
102
- "colors",
103
- "colorspace",
104
- "colourspace",
105
- "color_threshold",
106
- "combine",
107
- "combine_options",
108
- "comment",
109
- "compare",
110
- "complex",
111
- "compose",
112
- "composite",
113
- "compress",
114
- "connected_components",
115
- "contrast",
116
- "contrast_stretch",
117
- "convert",
118
- "convolve",
119
- "copy",
120
- "crop",
121
- "cycle",
122
- "deconstruct",
123
- "define",
124
- "delay",
125
- "delete",
126
- "density",
127
- "depth",
128
- "descend",
129
- "deskew",
130
- "despeckle",
131
- "direction",
132
- "displace",
133
- "dispose",
134
- "dissimilarity_threshold",
135
- "dissolve",
136
- "distort",
137
- "dither",
138
- "draw",
139
- "duplicate",
140
- "edge",
141
- "emboss",
142
- "encoding",
143
- "endian",
144
- "enhance",
145
- "equalize",
146
- "evaluate",
147
- "evaluate_sequence",
148
- "extent",
149
- "extract",
150
- "family",
151
- "features",
152
- "fft",
153
- "fill",
154
- "filter",
155
- "flatten",
156
- "flip",
157
- "floodfill",
158
- "flop",
159
- "font",
160
- "foreground",
161
- "format",
162
- "frame",
163
- "function",
164
- "fuzz",
165
- "fx",
166
- "gamma",
167
- "gaussian_blur",
168
- "geometry",
169
- "gravity",
170
- "grayscale",
171
- "green_primary",
172
- "hald_clut",
173
- "highlight_color",
174
- "hough_lines",
175
- "iconGeometry",
176
- "iconic",
177
- "identify",
178
- "ift",
179
- "illuminant",
180
- "immutable",
181
- "implode",
182
- "insert",
183
- "intensity",
184
- "intent",
185
- "interlace",
186
- "interline_spacing",
187
- "interpolate",
188
- "interpolative_resize",
189
- "interword_spacing",
190
- "kerning",
191
- "kmeans",
192
- "kuwahara",
193
- "label",
194
- "lat",
195
- "layers",
196
- "level",
197
- "level_colors",
198
- "limit",
199
- "limits",
200
- "linear_stretch",
201
- "linewidth",
202
- "liquid_rescale",
203
- "list",
204
- "loader",
205
- "log",
206
- "loop",
207
- "lowlight_color",
208
- "magnify",
209
- "map",
210
- "mattecolor",
211
- "median",
212
- "mean_shift",
213
- "metric",
214
- "mode",
215
- "modulate",
216
- "moments",
217
- "monitor",
218
- "monochrome",
219
- "morph",
220
- "morphology",
221
- "mosaic",
222
- "motion_blur",
223
- "name",
224
- "negate",
225
- "noise",
226
- "normalize",
227
- "opaque",
228
- "ordered_dither",
229
- "orient",
230
- "page",
231
- "paint",
232
- "pause",
233
- "perceptible",
234
- "ping",
235
- "pointsize",
236
- "polaroid",
237
- "poly",
238
- "posterize",
239
- "precision",
240
- "preview",
241
- "process",
242
- "quality",
243
- "quantize",
244
- "quiet",
245
- "radial_blur",
246
- "raise",
247
- "random_threshold",
248
- "range_threshold",
249
- "red_primary",
250
- "regard_warnings",
251
- "region",
252
- "remote",
253
- "render",
254
- "repage",
255
- "resample",
256
- "resize",
257
- "resize_to_fill",
258
- "resize_to_fit",
259
- "resize_to_limit",
260
- "resize_and_pad",
261
- "respect_parentheses",
262
- "reverse",
263
- "roll",
264
- "rotate",
265
- "sample",
266
- "sampling_factor",
267
- "saver",
268
- "scale",
269
- "scene",
270
- "screen",
271
- "seed",
272
- "segment",
273
- "selective_blur",
274
- "separate",
275
- "sepia_tone",
276
- "shade",
277
- "shadow",
278
- "shared_memory",
279
- "sharpen",
280
- "shave",
281
- "shear",
282
- "sigmoidal_contrast",
283
- "silent",
284
- "similarity_threshold",
285
- "size",
286
- "sketch",
287
- "smush",
288
- "snaps",
289
- "solarize",
290
- "sort_pixels",
291
- "sparse_color",
292
- "splice",
293
- "spread",
294
- "statistic",
295
- "stegano",
296
- "stereo",
297
- "storage_type",
298
- "stretch",
299
- "strip",
300
- "stroke",
301
- "strokewidth",
302
- "style",
303
- "subimage_search",
304
- "swap",
305
- "swirl",
306
- "synchronize",
307
- "taint",
308
- "text_font",
309
- "threshold",
310
- "thumbnail",
311
- "tile_offset",
312
- "tint",
313
- "title",
314
- "transform",
315
- "transparent",
316
- "transparent_color",
317
- "transpose",
318
- "transverse",
319
- "treedepth",
320
- "trim",
321
- "type",
322
- "undercolor",
323
- "unique_colors",
324
- "units",
325
- "unsharp",
326
- "update",
327
- "valid_image",
328
- "view",
329
- "vignette",
330
- "virtual_pixel",
331
- "visual",
332
- "watermark",
333
- "wave",
334
- "wavelet_denoise",
335
- "weight",
336
- "white_balance",
337
- "white_point",
338
- "white_threshold",
339
- "window",
340
- "window_group",
341
- ]
342
- mattr_accessor :unsupported_image_processing_arguments
54
+ mattr_accessor :service_urls_expire_in, default: 5.minutes
55
+ mattr_accessor :routes_prefix, default: "/rails/active_storage"
56
+
57
+ module Transformers
58
+ extend ActiveSupport::Autoload
59
+
60
+ autoload :Transformer
61
+ autoload :ImageProcessingTransformer
62
+ autoload :MiniMagickTransformer
63
+ end
343
64
  end
@@ -12,4 +12,11 @@ namespace :active_storage do
12
12
  Rake::Task["app:active_storage:install:migrations"].invoke
13
13
  end
14
14
  end
15
+
16
+ # desc "Copy over the migrations needed to the application upgrading"
17
+ task update: :environment do
18
+ ENV["MIGRATIONS_PATH"] = "db/update_migrate"
19
+
20
+ Rake::Task["active_storage:install"].invoke
21
+ end
15
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.8.1
4
+ version: 6.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-12 00:00:00.000000000 Z
11
+ date: 2019-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.2.8.1
19
+ version: 6.0.0.beta1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.2.8.1
26
+ version: 6.0.0.beta1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 5.2.8.1
33
+ version: 6.0.0.beta1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 5.2.8.1
40
+ version: 6.0.0.beta1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: marcel
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.0.0
47
+ version: 0.3.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.0.0
54
+ version: 0.3.1
55
55
  description: Attach cloud and local files in Rails applications.
56
56
  email: david@loudthinking.com
57
57
  executables: []
@@ -68,6 +68,7 @@ files:
68
68
  - app/controllers/active_storage/disk_controller.rb
69
69
  - app/controllers/active_storage/representations_controller.rb
70
70
  - app/controllers/concerns/active_storage/set_blob.rb
71
+ - app/controllers/concerns/active_storage/set_current.rb
71
72
  - app/javascript/activestorage/blob_record.js
72
73
  - app/javascript/activestorage/blob_upload.js
73
74
  - app/javascript/activestorage/direct_upload.js
@@ -87,21 +88,28 @@ files:
87
88
  - app/models/active_storage/blob/representable.rb
88
89
  - app/models/active_storage/current.rb
89
90
  - app/models/active_storage/filename.rb
90
- - app/models/active_storage/filename/parameters.rb
91
91
  - app/models/active_storage/preview.rb
92
92
  - app/models/active_storage/variant.rb
93
93
  - app/models/active_storage/variation.rb
94
94
  - config/routes.rb
95
95
  - db/migrate/20170806125915_create_active_storage_tables.rb
96
+ - db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb
96
97
  - lib/active_storage.rb
97
98
  - lib/active_storage/analyzer.rb
98
99
  - lib/active_storage/analyzer/image_analyzer.rb
99
100
  - lib/active_storage/analyzer/null_analyzer.rb
100
101
  - lib/active_storage/analyzer/video_analyzer.rb
101
102
  - lib/active_storage/attached.rb
102
- - lib/active_storage/attached/macros.rb
103
+ - lib/active_storage/attached/changes.rb
104
+ - lib/active_storage/attached/changes/create_many.rb
105
+ - lib/active_storage/attached/changes/create_one.rb
106
+ - lib/active_storage/attached/changes/create_one_of_many.rb
107
+ - lib/active_storage/attached/changes/delete_many.rb
108
+ - lib/active_storage/attached/changes/delete_one.rb
103
109
  - lib/active_storage/attached/many.rb
110
+ - lib/active_storage/attached/model.rb
104
111
  - lib/active_storage/attached/one.rb
112
+ - lib/active_storage/downloader.rb
105
113
  - lib/active_storage/downloading.rb
106
114
  - lib/active_storage/engine.rb
107
115
  - lib/active_storage/errors.rb
@@ -111,6 +119,7 @@ files:
111
119
  - lib/active_storage/previewer/mupdf_previewer.rb
112
120
  - lib/active_storage/previewer/poppler_pdf_previewer.rb
113
121
  - lib/active_storage/previewer/video_previewer.rb
122
+ - lib/active_storage/reflection.rb
114
123
  - lib/active_storage/service.rb
115
124
  - lib/active_storage/service/azure_storage_service.rb
116
125
  - lib/active_storage/service/configurator.rb
@@ -118,15 +127,18 @@ files:
118
127
  - lib/active_storage/service/gcs_service.rb
119
128
  - lib/active_storage/service/mirror_service.rb
120
129
  - lib/active_storage/service/s3_service.rb
130
+ - lib/active_storage/transformers/image_processing_transformer.rb
131
+ - lib/active_storage/transformers/mini_magick_transformer.rb
132
+ - lib/active_storage/transformers/transformer.rb
121
133
  - lib/active_storage/version.rb
122
134
  - lib/tasks/activestorage.rake
123
135
  homepage: http://rubyonrails.org
124
136
  licenses:
125
137
  - MIT
126
138
  metadata:
127
- source_code_uri: https://github.com/rails/rails/tree/v5.2.8.1/activestorage
128
- changelog_uri: https://github.com/rails/rails/blob/v5.2.8.1/activestorage/CHANGELOG.md
129
- post_install_message:
139
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.0.beta1/activestorage
140
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.0.beta1/activestorage/CHANGELOG.md
141
+ post_install_message:
130
142
  rdoc_options: []
131
143
  require_paths:
132
144
  - lib
@@ -134,15 +146,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
146
  requirements:
135
147
  - - ">="
136
148
  - !ruby/object:Gem::Version
137
- version: 2.2.2
149
+ version: 2.5.0
138
150
  required_rubygems_version: !ruby/object:Gem::Requirement
139
151
  requirements:
140
- - - ">="
152
+ - - ">"
141
153
  - !ruby/object:Gem::Version
142
- version: '0'
154
+ version: 1.3.1
143
155
  requirements: []
144
- rubygems_version: 3.3.3
145
- signing_key:
156
+ rubygems_version: 3.0.1
157
+ signing_key:
146
158
  specification_version: 4
147
159
  summary: Local and cloud file storage framework.
148
160
  test_files: []
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ActiveStorage::Filename::Parameters #:nodoc:
4
- attr_reader :filename
5
-
6
- def initialize(filename)
7
- @filename = filename
8
- end
9
-
10
- def combined
11
- "#{ascii}; #{utf8}"
12
- end
13
-
14
- TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/
15
-
16
- def ascii
17
- 'filename="' + percent_escape(I18n.transliterate(filename.sanitized), TRADITIONAL_ESCAPED_CHAR) + '"'
18
- end
19
-
20
- RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/
21
-
22
- def utf8
23
- "filename*=UTF-8''" + percent_escape(filename.sanitized, RFC_5987_ESCAPED_CHAR)
24
- end
25
-
26
- def to_s
27
- combined
28
- end
29
-
30
- private
31
- def percent_escape(string, pattern)
32
- string.gsub(pattern) do |char|
33
- char.bytes.map { |byte| "%%%02X" % byte }.join
34
- end
35
- end
36
- end