Class: Resizing::CarrierWave::Storage::File

Inherits:
Object
  • Object
show all
Includes:
CarrierWave::Utilities::Uri
Defined in:
lib/resizing/carrier_wave/storage/file.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

DEFAULT_CONTENT_TYPE =

Fallback content type used when the type cannot be determined from the file name.

'application/octet-stream'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uploader, identifier = nil) ⇒ File

Returns a new instance of File.



15
16
17
18
19
20
21
# File 'lib/resizing/carrier_wave/storage/file.rb', line 15

def initialize(uploader, identifier = nil)
  @uploader = uploader
  @content_type = nil
  @public_id = Resizing::PublicId.new(identifier)
  @metadata = nil
  @metadata_fetched = false
end

Instance Attribute Details

#public_id ⇒ Object (readonly)

Returns the value of attribute public_id.



13
14
15
# File 'lib/resizing/carrier_wave/storage/file.rb', line 13

def public_id
  @public_id
end

Instance Method Details

#attributes ⇒ Object

Metadata of the stored image, as returned by the Resizing metadata API.

Returns

[Hash, nil] metadata hash, or nil when the image is unknown/unreachable



28
29
30
# File 'lib/resizing/carrier_wave/storage/file.rb', line 28

def attributes
  
end

#authenticated_url(_options = {}) ⇒ Object



32
33
34
# File 'lib/resizing/carrier_wave/storage/file.rb', line 32

def authenticated_url(_options = {})
  nil
end

#content_type ⇒ Object

NOTE: Called through CarrierWave's Uploader::Proxy#content_type, so it must not raise. Falls back to the metadata API when the content type is not known locally (e.g. the model was reloaded from the DB), and returns nil if it cannot be fetched.



39
40
41
# File 'lib/resizing/carrier_wave/storage/file.rb', line 39

def content_type
  @content_type ||=  && ['content_type']
end

#current_path ⇒ Object Also known as: path



108
109
110
111
112
113
114
# File 'lib/resizing/carrier_wave/storage/file.rb', line 108

def current_path
  # Return the path from @public_id if set (for retrieve scenarios),
  # otherwise fall back to reading from model
  return @public_id.to_s if @public_id.present?

  @current_path = model.send :read_attribute, serialization_column
end

#delete ⇒ Object

rubocop:disable Metrics/AbcSize

Raises:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/resizing/carrier_wave/storage/file.rb', line 44

def delete
  # Use the identifier from constructor if available, otherwise try to get from model
  if @public_id.present?
    # Already set from constructor or retrieve
  elsif model.respond_to?(:attribute_was)
    # Try to get the value before changes (for remove! scenario)
    column_value = model.attribute_was(serialization_column) || model.send(:read_attribute,
                                                                           serialization_column)
    @public_id = Resizing::PublicId.new(column_value)
  else
    column_value = model.send(:read_attribute, serialization_column)
    @public_id = Resizing::PublicId.new(column_value)
  end

  return if @public_id.empty?

  resp = client.delete(@public_id.image_id)

  # NOTE: 削除時のカラムクリアは以下の理由で必要:
  # - 画像更新時: 古い画像IDと新しい画像IDが異なるため、古い画像削除時に新しいIDを消さないようにする
  # - 明示的なremove!時: カラムをnilにする必要がある
  # - clear_column_if_current_imageは削除される画像IDと現在のカラム値を比較して判断
  if resp['error'] == 'ActiveRecord::RecordNotFound' # 404 not found
    clear_column_if_current_image
    return
  end

  if @public_id.image_id == resp['id']
    clear_column_if_current_image
    return
  end

  raise APIError, "raise someone error:#{resp.inspect}"
end

#exists? ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/resizing/carrier_wave/storage/file.rb', line 104

def exists?
  !.nil?
end

#extension ⇒ Object

rubocop:enable Metrics/AbcSize

Raises:

  • (NotImplementedError)


80
81
82
# File 'lib/resizing/carrier_wave/storage/file.rb', line 80

def extension
  raise NotImplementedError, 'this method is do not used. maybe'
end

#name(_options = {}) ⇒ Object

rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity



167
168
169
170
# File 'lib/resizing/carrier_wave/storage/file.rb', line 167

def name(_options = {})
  @public_id = PublicId.new(model.send(:read_attribute, serialization_column))
  CGI.unescape(@public_id.filename)
end

#read ⇒ Object

Read content of file from service

NOTE: Resizing keeps no local copy of the stored image and this gem has no API to download the binary (Resizing::Client#get is not implemented either), so reading the content would require a new download API. It is not called by the regular CarrierWave flow (only through Uploader::Proxy#read, i.e. explicitly by the application), so it raises instead of silently returning nil.

Raises:

  • (NotImplementedError)


92
93
94
# File 'lib/resizing/carrier_wave/storage/file.rb', line 92

def read
  raise NotImplementedError, 'Resizing does not support reading the stored image content'
end

#retrieve(identifier) ⇒ Object

NOTE: Resizing::CarrierWave#file calls this on every access, so the cached metadata is only dropped when the identifier actually changes.



178
179
180
181
182
# File 'lib/resizing/carrier_wave/storage/file.rb', line 178

def retrieve(identifier)
  new_public_id = Resizing::PublicId.new(identifier)
   unless new_public_id.to_s == @public_id.to_s
  @public_id = new_public_id
end

#size ⇒ Object

NOTE: Called through CarrierWave's Uploader::Proxy#size, so it must not raise. The size is only known to the Resizing service, so it comes from the metadata API. Returns 0 when it cannot be fetched, following Uploader::Proxy#size, which itself falls back to 0, so that the return value is always an Integer.



100
101
102
# File 'lib/resizing/carrier_wave/storage/file.rb', line 100

def size
  ( && ['size']) || 0
end

#store(new_file) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/resizing/carrier_wave/storage/file.rb', line 119

def store(new_file)
  if new_file.is_a?(self.class)
    # new_file.copy_to(path)
    raise NotImplementedError, 'new file is required duplicating'
  end

  @content_type ||= if new_file.respond_to? :content_type
                      new_file.content_type
                    else
                      # Guess content-type from extension. MIME::Types returns an empty
                      # list for extensions it does not know (e.g. `.url`), so fall back
                      # to the generic binary type instead of raising NoMethodError.
                      guess_content_type(new_file.path)
                    end

  original_filename = new_file.try(:original_filename) || new_file.try(:filename) || new_file.try(:path)
  original_filename = ::File.basename(original_filename) if original_filename.present?

  content = if new_file.respond_to?(:to_io)
              new_file.to_io.tap(&:rewind)
            elsif new_file.respond_to?(:read) && new_file.respond_to?(:rewind)
              # Pass the IO object itself, not the read result
              new_file.rewind
              new_file
            else
              new_file
            end

  @response = Resizing.post(content, { content_type: @content_type, filename: original_filename })
  @public_id = Resizing::PublicId.new(@response['public_id'])
  @content_type = @response['content_type']

  # NOTE: 理想的にはStorage::File内でモデルのカラムをいじらず、CarrierWaveに任せるべきだが、
  # 現在の実装では以下の理由で必要:
  # - CarrierWaveは write_uploader(column, mounter.identifiers.first) でカラムを更新
  # - mounter.identifiers -> uploaders.map(&:identifier) -> storage.identifier -> uploader.filename
  # - resizing-gemの filenameメソッドは read_column を返す(既存のカラム値)
  # - そのため、CarrierWaveに任せると旧い値が書き戻されてしまう
  # TODO: これを修正するには、Remote#identifierをオーバーライドして@public_id.to_sを返すか、
  #       uploader.filenameの実装を変更する必要がある
  # save new value to model class
  model.send :write_attribute, serialization_column, @public_id.to_s

  true
end