shrine 3.1.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/README.md +11 -4
  4. data/doc/advantages.md +4 -4
  5. data/doc/attacher.md +2 -2
  6. data/doc/carrierwave.md +24 -12
  7. data/doc/changing_derivatives.md +1 -1
  8. data/doc/changing_location.md +6 -5
  9. data/doc/design.md +134 -85
  10. data/doc/direct_s3.md +26 -0
  11. data/doc/external/articles.md +57 -45
  12. data/doc/external/extensions.md +41 -35
  13. data/doc/external/misc.md +23 -8
  14. data/doc/getting_started.md +156 -85
  15. data/doc/metadata.md +80 -44
  16. data/doc/multiple_files.md +1 -1
  17. data/doc/paperclip.md +28 -9
  18. data/doc/plugins/add_metadata.md +112 -35
  19. data/doc/plugins/atomic_helpers.md +41 -3
  20. data/doc/plugins/backgrounding.md +12 -2
  21. data/doc/plugins/column.md +36 -7
  22. data/doc/plugins/default_url.md +6 -3
  23. data/doc/plugins/derivatives.md +83 -44
  24. data/doc/plugins/download_endpoint.md +5 -5
  25. data/doc/plugins/dynamic_storage.md +1 -1
  26. data/doc/plugins/entity.md +12 -4
  27. data/doc/plugins/form_assign.md +5 -5
  28. data/doc/plugins/included.md +25 -5
  29. data/doc/plugins/infer_extension.md +9 -0
  30. data/doc/plugins/instrumentation.md +1 -1
  31. data/doc/plugins/metadata_attributes.md +1 -0
  32. data/doc/plugins/mirroring.md +1 -1
  33. data/doc/plugins/model.md +8 -3
  34. data/doc/plugins/persistence.md +10 -1
  35. data/doc/plugins/remote_url.md +6 -1
  36. data/doc/plugins/remove_invalid.md +9 -1
  37. data/doc/plugins/sequel.md +1 -1
  38. data/doc/plugins/store_dimensions.md +10 -0
  39. data/doc/plugins/type_predicates.md +96 -0
  40. data/doc/plugins/upload_endpoint.md +1 -1
  41. data/doc/plugins/upload_options.md +1 -1
  42. data/doc/plugins/url_options.md +4 -4
  43. data/doc/plugins/validation.md +14 -4
  44. data/doc/plugins/versions.md +7 -7
  45. data/doc/processing.md +287 -123
  46. data/doc/refile.md +9 -9
  47. data/doc/release_notes/2.8.0.md +1 -1
  48. data/doc/release_notes/3.0.0.md +1 -1
  49. data/doc/release_notes/3.2.0.md +96 -0
  50. data/doc/release_notes/3.2.1.md +31 -0
  51. data/doc/release_notes/3.2.2.md +14 -0
  52. data/doc/release_notes/3.3.0.md +105 -0
  53. data/doc/release_notes/3.4.0.md +35 -0
  54. data/doc/securing_uploads.md +2 -2
  55. data/doc/storage/memory.md +19 -0
  56. data/doc/storage/s3.md +104 -77
  57. data/doc/testing.md +12 -2
  58. data/doc/upgrading_to_3.md +99 -53
  59. data/lib/shrine.rb +9 -8
  60. data/lib/shrine/attacher.rb +20 -10
  61. data/lib/shrine/attachment.rb +2 -2
  62. data/lib/shrine/plugins.rb +22 -0
  63. data/lib/shrine/plugins/activerecord.rb +3 -3
  64. data/lib/shrine/plugins/add_metadata.rb +20 -5
  65. data/lib/shrine/plugins/backgrounding.rb +2 -2
  66. data/lib/shrine/plugins/default_url.rb +1 -1
  67. data/lib/shrine/plugins/derivation_endpoint.rb +13 -8
  68. data/lib/shrine/plugins/derivatives.rb +59 -30
  69. data/lib/shrine/plugins/determine_mime_type.rb +5 -3
  70. data/lib/shrine/plugins/entity.rb +12 -11
  71. data/lib/shrine/plugins/instrumentation.rb +12 -18
  72. data/lib/shrine/plugins/mirroring.rb +8 -8
  73. data/lib/shrine/plugins/model.rb +3 -3
  74. data/lib/shrine/plugins/presign_endpoint.rb +16 -4
  75. data/lib/shrine/plugins/pretty_location.rb +1 -1
  76. data/lib/shrine/plugins/processing.rb +1 -1
  77. data/lib/shrine/plugins/refresh_metadata.rb +2 -2
  78. data/lib/shrine/plugins/remote_url.rb +3 -3
  79. data/lib/shrine/plugins/remove_attachment.rb +5 -0
  80. data/lib/shrine/plugins/remove_invalid.rb +10 -5
  81. data/lib/shrine/plugins/sequel.rb +1 -1
  82. data/lib/shrine/plugins/store_dimensions.rb +4 -2
  83. data/lib/shrine/plugins/type_predicates.rb +113 -0
  84. data/lib/shrine/plugins/upload_endpoint.rb +10 -5
  85. data/lib/shrine/plugins/upload_options.rb +2 -2
  86. data/lib/shrine/plugins/url_options.rb +2 -2
  87. data/lib/shrine/plugins/validation.rb +9 -7
  88. data/lib/shrine/storage/linter.rb +4 -4
  89. data/lib/shrine/storage/memory.rb +5 -3
  90. data/lib/shrine/storage/s3.rb +117 -38
  91. data/lib/shrine/version.rb +1 -1
  92. data/shrine.gemspec +8 -8
  93. metadata +42 -34
@@ -22,8 +22,8 @@ class Shrine
22
22
  # Shorthand for `Attachment.new`.
23
23
  #
24
24
  # Shrine::Attachment[:image]
25
- def [](*args)
26
- new(*args)
25
+ def [](*args, **options)
26
+ new(*args, **options)
27
27
  end
28
28
  end
29
29
 
@@ -18,6 +18,28 @@ class Shrine
18
18
  plugin
19
19
  end
20
20
 
21
+ # Delegate call to the plugin in a way that works across Ruby versions.
22
+ def self.load_dependencies(plugin, uploader, *args, **kwargs, &block)
23
+ return unless plugin.respond_to?(:load_dependencies)
24
+
25
+ if kwargs.any?
26
+ plugin.load_dependencies(uploader, *args, **kwargs, &block)
27
+ else
28
+ plugin.load_dependencies(uploader, *args, &block)
29
+ end
30
+ end
31
+
32
+ # Delegate call to the plugin in a way that works across Ruby versions.
33
+ def self.configure(plugin, uploader, *args, **kwargs, &block)
34
+ return unless plugin.respond_to?(:configure)
35
+
36
+ if kwargs.any?
37
+ plugin.configure(uploader, *args, **kwargs, &block)
38
+ else
39
+ plugin.configure(uploader, *args, &block)
40
+ end
41
+ end
42
+
21
43
  # Register the given plugin with Shrine, so that it can be loaded using
22
44
  # `Shrine.plugin` with a symbol. Should be used by plugin files. Example:
23
45
  #
@@ -55,7 +55,7 @@ class Shrine
55
55
  # reload the attacher on record reload
56
56
  define_method :reload do |*args|
57
57
  result = super(*args)
58
- instance_variable_set(:"@#{name}_attacher", nil)
58
+ send(:"#{name}_attacher").reload if instance_variable_defined?(:"@#{name}_attacher")
59
59
  result
60
60
  end
61
61
  end
@@ -75,8 +75,8 @@ class Shrine
75
75
  def activerecord_validate
76
76
  return unless respond_to?(:errors)
77
77
 
78
- errors.each do |message|
79
- record.errors.add(name, *message)
78
+ errors.each do |(type, options)|
79
+ record.errors.add(name, type, **options.to_h)
80
80
  end
81
81
  end
82
82
 
@@ -9,8 +9,8 @@ class Shrine
9
9
  end
10
10
 
11
11
  module ClassMethods
12
- def add_metadata(name = nil, &block)
13
- opts[:add_metadata][:definitions] << [name, block]
12
+ def add_metadata(name = nil, **options, &block)
13
+ opts[:add_metadata][:definitions] << [name, options, block]
14
14
 
15
15
  metadata_method(name) if name
16
16
  end
@@ -40,10 +40,12 @@ class Shrine
40
40
  private
41
41
 
42
42
  def extract_custom_metadata(io, **options)
43
- opts[:add_metadata][:definitions].each do |name, block|
44
- result = instance_exec(io, options, &block)
43
+ opts[:add_metadata][:definitions].each do |name, definition_options, block|
44
+ result = instance_exec(io, **options, &block)
45
45
 
46
- if name
46
+ if result.nil? && definition_options[:skip_nil]
47
+ # Do not store this metadata
48
+ elsif name
47
49
  options[:metadata].merge! name.to_s => result
48
50
  else
49
51
  options[:metadata].merge! result.transform_keys(&:to_s) if result
@@ -54,6 +56,19 @@ class Shrine
54
56
  end
55
57
  end
56
58
  end
59
+
60
+ module AttacherMethods
61
+ def add_metadata(new_metadata, &block)
62
+ file!.add_metadata(new_metadata, &block)
63
+ set(file) # trigger model write
64
+ end
65
+ end
66
+
67
+ module FileMethods
68
+ def add_metadata(new_metadata, &block)
69
+ @metadata = @metadata.merge(new_metadata, &block)
70
+ end
71
+ end
57
72
  end
58
73
 
59
74
  register_plugin(:add_metadata, AddMetadata)
@@ -36,8 +36,8 @@ class Shrine
36
36
 
37
37
  module AttacherMethods
38
38
  # Inherits global hooks if defined.
39
- def initialize(*args)
40
- super
39
+ def initialize(**args)
40
+ super(**args)
41
41
  @destroy_block = self.class.destroy_block
42
42
  @promote_block = self.class.promote_block
43
43
  end
@@ -25,7 +25,7 @@ class Shrine
25
25
  def default_url(**options)
26
26
  return unless default_url_block
27
27
 
28
- url = instance_exec(options, &default_url_block)
28
+ url = instance_exec(**options, &default_url_block)
29
29
 
30
30
  [*default_url_host, url].join
31
31
  end
@@ -392,6 +392,7 @@ class Shrine
392
392
  options[:type] = request.params["type"] if request.params["type"]
393
393
  options[:disposition] = request.params["disposition"] if request.params["disposition"]
394
394
  options[:filename] = request.params["filename"] if request.params["filename"]
395
+ options[:version] = request.params["version"] if request.params["version"]
395
396
  options[:expires_in] = expires_in(request) if request.params["expires_at"]
396
397
 
397
398
  derivation = uploaded_file.derivation(name, *args, **options)
@@ -475,9 +476,9 @@ class Shrine
475
476
  file_response(derivative, env)
476
477
  end
477
478
 
478
- # Generates a Rack response triple from a local file using `Rack::File`.
479
- # Fills in `Content-Type` and `Content-Disposition` response headers from
480
- # derivation options and file extension of the derivation result.
479
+ # Generates a Rack response triple from a local file. Fills in
480
+ # `Content-Type` and `Content-Disposition` response headers from derivation
481
+ # options and file extension of the derivation result.
481
482
  def file_response(file, env)
482
483
  response = rack_file_response(file.path, env)
483
484
 
@@ -511,7 +512,7 @@ class Shrine
511
512
  end
512
513
 
513
514
  if upload_redirect
514
- redirect_url = uploaded_file.url(upload_redirect_url_options)
515
+ redirect_url = uploaded_file.url(**upload_redirect_url_options)
515
516
 
516
517
  [302, { "Location" => redirect_url }, []]
517
518
  else
@@ -528,10 +529,14 @@ class Shrine
528
529
  end
529
530
  end
530
531
 
531
- # We call `Rack::File` with no default `Content-Type`, and make sure we
532
+ # We call `Rack::Files` with no default `Content-Type`, and make sure we
532
533
  # stay compatible with both Rack 2.x and 1.6.x.
533
534
  def rack_file_response(path, env)
534
- server = Rack::File.new("", {}, nil)
535
+ if Rack.release >= "2.1"
536
+ server = Rack::Files.new("", {}, nil)
537
+ else
538
+ server = Rack::File.new("", {}, nil)
539
+ end
535
540
 
536
541
  if Rack.release > "2"
537
542
  server.serving(Rack::Request.new(env), path)
@@ -598,7 +603,7 @@ class Shrine
598
603
  def instrument_derivation(&block)
599
604
  return yield unless shrine_class.respond_to?(:instrument)
600
605
 
601
- shrine_class.instrument(:derivation, derivation: derivation, &block)
606
+ shrine_class.instrument(:derivation, { derivation: derivation }, &block)
602
607
  end
603
608
 
604
609
  # Massages the derivation result, ensuring it's opened in binary mode,
@@ -734,7 +739,7 @@ class Shrine
734
739
  def verify_signature(string, signature)
735
740
  if signature.nil?
736
741
  fail InvalidSignature, "missing \"signature\" param"
737
- elsif signature != generate_signature(string)
742
+ elsif !Rack::Utils.secure_compare(signature, generate_signature(string))
738
743
  fail InvalidSignature, "provided signature does not match the calculated signature"
739
744
  end
740
745
  end
@@ -4,8 +4,6 @@ class Shrine
4
4
  module Plugins
5
5
  # Documentation can be found on https://shrinerb.com/docs/plugins/derivatives
6
6
  module Derivatives
7
- NOOP_PROCESSOR = -> (*) { Hash.new }
8
-
9
7
  LOG_SUBSCRIBER = -> (event) do
10
8
  Shrine.logger.info "Derivatives (#{event.duration}ms) – #{{
11
9
  processor: event[:processor],
@@ -21,7 +19,7 @@ class Shrine
21
19
  end
22
20
 
23
21
  def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
24
- uploader.opts[:derivatives] ||= { processors: {}, storage: proc { store_key } }
22
+ uploader.opts[:derivatives] ||= { processors: {}, processor_settings: {}, storage: proc { store_key } }
25
23
  uploader.opts[:derivatives].merge!(opts)
26
24
 
27
25
  # instrumentation plugin integration
@@ -40,8 +38,8 @@ class Shrine
40
38
  def define_model_methods(name)
41
39
  super if defined?(super)
42
40
 
43
- define_method(:"#{name}_derivatives!") do |*args|
44
- send(:"#{name}_attacher").create_derivatives(*args)
41
+ define_method(:"#{name}_derivatives!") do |*args, **options|
42
+ send(:"#{name}_attacher").create_derivatives(*args, **options)
45
43
  end
46
44
  end
47
45
  end
@@ -52,20 +50,37 @@ class Shrine
52
50
  # Attacher.derivatives_processor :thumbnails do |original|
53
51
  # # ...
54
52
  # end
55
- def derivatives_processor(name = :default, &block)
53
+ #
54
+ # By default, Shrine will convert the source IO object into a file
55
+ # before it's passed to the processor block. You can set `download:
56
+ # false` to pass the source IO object to the processor block as is.
57
+ #
58
+ # Attacher.derivatives_processor :thumbnails, download: false do |original|
59
+ # # ...
60
+ # end
61
+ #
62
+ # This can be useful if you'd like to defer or avoid a possibly
63
+ # expensive download operation for processor logic that does not
64
+ # require it.
65
+ def derivatives_processor(name = :default, download: true, &block)
56
66
  if block
57
- shrine_class.opts[:derivatives][:processors][name.to_sym] = block
67
+ shrine_class.derivatives_options[:processors][name.to_sym] = block
68
+ shrine_class.derivatives_options[:processor_settings][name.to_sym] = { download: download }
58
69
  else
59
- processor = shrine_class.opts[:derivatives][:processors][name.to_sym]
60
- processor ||= NOOP_PROCESSOR if name == :default
61
-
62
- fail Error, "derivatives processor #{name.inspect} not registered" unless processor
63
-
64
- processor
70
+ shrine_class.derivatives_options[:processors].fetch(name.to_sym) do
71
+ fail Error, "derivatives processor #{name.inspect} not registered" unless name == :default
72
+ end
65
73
  end
66
74
  end
67
75
  alias derivatives derivatives_processor
68
76
 
77
+ # Returns settings for the given derivatives processor.
78
+ #
79
+ # Attacher.derivatives_processor_settings(:thumbnails) #=> { download: true }
80
+ def derivatives_processor_settings(name)
81
+ shrine_class.derivatives_options[:processor_settings][name.to_sym] || {}
82
+ end
83
+
69
84
  # Specifies default storage to which derivatives will be uploaded.
70
85
  #
71
86
  # Attacher.derivatives_storage :other_store
@@ -79,9 +94,9 @@ class Shrine
79
94
  # end
80
95
  def derivatives_storage(storage_key = nil, &block)
81
96
  if storage_key || block
82
- shrine_class.opts[:derivatives][:storage] = storage_key || block
97
+ shrine_class.derivatives_options[:storage] = storage_key || block
83
98
  else
84
- shrine_class.opts[:derivatives][:storage]
99
+ shrine_class.derivatives_options[:storage]
85
100
  end
86
101
  end
87
102
  end
@@ -147,6 +162,7 @@ class Shrine
147
162
  def promote(**options)
148
163
  super
149
164
  promote_derivatives
165
+ create_derivatives if create_derivatives_on_promote?
150
166
  end
151
167
 
152
168
  # Uploads any cached derivatives to permanent storage.
@@ -227,8 +243,6 @@ class Shrine
227
243
  # hash[:thumb] #=> #<Shrine::UploadedFile>
228
244
  def upload_derivatives(files, **options)
229
245
  map_derivative(files) do |path, file|
230
- path = derivative_path(path)
231
-
232
246
  upload_derivative(path, file, **options)
233
247
  end
234
248
  end
@@ -238,6 +252,7 @@ class Shrine
238
252
  # hash = attacher.upload_derivative(:thumb, thumb)
239
253
  # hash[:thumb] #=> #<Shrine::UploadedFile>
240
254
  def upload_derivative(path, file, storage: nil, **options)
255
+ path = derivative_path(path)
241
256
  storage ||= derivative_storage(path)
242
257
 
243
258
  file.open if file.is_a?(Tempfile) # refresh file descriptor
@@ -269,8 +284,10 @@ class Shrine
269
284
 
270
285
  source ||= file!
271
286
 
272
- if source.is_a?(UploadedFile)
273
- source.download do |file|
287
+ processor_settings = self.class.derivatives_processor_settings(processor_name) || {}
288
+
289
+ if processor_settings[:download]
290
+ shrine_class.with_file(source) do |file|
274
291
  _process_derivatives(processor_name, file, **options)
275
292
  end
276
293
  else
@@ -374,7 +391,7 @@ class Shrine
374
391
  # attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
375
392
  def set_derivatives(derivatives)
376
393
  self.derivatives = derivatives
377
- set file # trigger model writing
394
+ set file # trigger model write
378
395
  derivatives
379
396
  end
380
397
 
@@ -442,7 +459,7 @@ class Shrine
442
459
  # attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
443
460
  # attacher.change(file)
444
461
  # attacher.derivatives #=> {}
445
- def change(*args)
462
+ def change(*)
446
463
  result = super
447
464
  set_derivatives({})
448
465
  result
@@ -463,8 +480,8 @@ class Shrine
463
480
  # Iterates through nested derivatives and maps results.
464
481
  #
465
482
  # attacher.map_derivative(derivatives) { |path, file| ... }
466
- def map_derivative(*args, &block)
467
- shrine_class.map_derivative(*args, &block)
483
+ def map_derivative(derivatives, **options, &block)
484
+ shrine_class.map_derivative(derivatives, **options, &block)
468
485
  end
469
486
 
470
487
  private
@@ -473,7 +490,9 @@ class Shrine
473
490
  def _process_derivatives(processor_name, source, **options)
474
491
  processor = self.class.derivatives_processor(processor_name)
475
492
 
476
- result = instrument_derivatives(processor_name, options) do
493
+ return {} unless processor
494
+
495
+ result = instrument_derivatives(processor_name, source, options) do
477
496
  instance_exec(source, **options, &processor)
478
497
  end
479
498
 
@@ -485,20 +504,20 @@ class Shrine
485
504
  end
486
505
 
487
506
  # Sends a `derivatives.shrine` event for instrumentation plugin.
488
- def instrument_derivatives(processor_name, processor_options, &block)
507
+ def instrument_derivatives(processor_name, source, processor_options, &block)
489
508
  return yield unless shrine_class.respond_to?(:instrument)
490
509
 
491
- shrine_class.instrument(
492
- :derivatives,
510
+ shrine_class.instrument(:derivatives, {
493
511
  processor: processor_name,
494
512
  processor_options: processor_options,
495
- &block
496
- )
513
+ io: source,
514
+ attacher: self,
515
+ }, &block)
497
516
  end
498
517
 
499
518
  # Returns symbolized array or single key.
500
519
  def derivative_path(path)
501
- path = path.map { |key| key.is_a?(String) ? key.to_sym : key }
520
+ path = Array(path).map { |key| key.is_a?(String) ? key.to_sym : key }
502
521
  path = path.first if path.one?
503
522
  path
504
523
  end
@@ -520,6 +539,11 @@ class Shrine
520
539
  o2
521
540
  end
522
541
  end
542
+
543
+ # Whether to automatically create derivatives on promotion
544
+ def create_derivatives_on_promote?
545
+ shrine_class.derivatives_options[:create_on_promote]
546
+ end
523
547
  end
524
548
 
525
549
  module ClassMethods
@@ -576,6 +600,11 @@ class Shrine
576
600
  yield path, object
577
601
  end
578
602
  end
603
+
604
+ # Returns derivatives plugin options.
605
+ def derivatives_options
606
+ opts[:derivatives]
607
+ end
579
608
  end
580
609
 
581
610
  module FileMethods
@@ -124,6 +124,8 @@ class Shrine
124
124
  require "fastimage"
125
125
 
126
126
  type = FastImage.type(io)
127
+ return 'image/svg+xml' if type == :svg
128
+
127
129
  "image/#{type}" if type
128
130
  end
129
131
 
@@ -141,7 +143,7 @@ class Shrine
141
143
  require "mimemagic"
142
144
 
143
145
  mime = MimeMagic.by_magic(io)
144
- mime.type if mime
146
+ mime&.type
145
147
  end
146
148
 
147
149
  def extract_with_marcel(io, options)
@@ -158,7 +160,7 @@ class Shrine
158
160
 
159
161
  if filename = extract_filename(io)
160
162
  mime_type = MIME::Types.of(filename).first
161
- mime_type.content_type if mime_type
163
+ mime_type&.content_type
162
164
  end
163
165
  end
164
166
 
@@ -167,7 +169,7 @@ class Shrine
167
169
 
168
170
  if filename = extract_filename(io)
169
171
  info = MiniMime.lookup_by_filename(filename)
170
- info.content_type if info
172
+ info&.content_type
171
173
  end
172
174
  end
173
175
 
@@ -41,27 +41,27 @@ class Shrine
41
41
  end
42
42
 
43
43
  # Returns the URL to the attached file.
44
- define_method :"#{name}_url" do |*args|
45
- send(:"#{name}_attacher").url(*args)
44
+ define_method :"#{name}_url" do |*args, **options|
45
+ send(:"#{name}_attacher").url(*args, **options)
46
46
  end
47
47
 
48
48
  # Returns an attacher instance.
49
49
  define_method :"#{name}_attacher" do |**options|
50
- attachment.send(:attacher, self, options)
50
+ attachment.send(:attacher, self, **options)
51
51
  end
52
52
  end
53
53
 
54
54
  # Returns the class attacher instance with loaded entity. It's not
55
55
  # memoized because the entity object could be frozen.
56
- def attacher(record, options)
57
- attacher = class_attacher(options)
56
+ def attacher(record, **options)
57
+ attacher = class_attacher(**options)
58
58
  attacher.load_entity(record, @name)
59
59
  attacher
60
60
  end
61
61
 
62
62
  # Creates an instance of the corresponding attacher class with set
63
63
  # name.
64
- def class_attacher(options)
64
+ def class_attacher(**options)
65
65
  attacher = shrine_class::Attacher.new(**@options, **options)
66
66
  attacher.instance_variable_set(:@name, @name)
67
67
  attacher
@@ -112,9 +112,15 @@ class Shrine
112
112
  # attacher.file #=> #<Shrine::UploadedFile>
113
113
  def reload
114
114
  read
115
+ @previous = nil
115
116
  self
116
117
  end
117
118
 
119
+ # Loads attachment from the entity attribute.
120
+ def read
121
+ load_column(read_attribute)
122
+ end
123
+
118
124
  # Returns a hash with entity attribute name and column data.
119
125
  #
120
126
  # attacher.column_values
@@ -134,11 +140,6 @@ class Shrine
134
140
 
135
141
  private
136
142
 
137
- # Loads attachment from the entity attribute.
138
- def read
139
- load_column(read_attribute)
140
- end
141
-
142
143
  # Reads value from the entity attribute.
143
144
  def read_attribute
144
145
  record.public_send(attribute)