Class: Configuration::Thumbnail

Inherits:
Object
  • Object
show all
Extended by:
Stats
Includes:
ClassLogging, ConditionalInclusion
Defined in:
lib/httpimagestore/configuration/thumbnailer.rb

Defined Under Namespace

Classes: ThumbnailSpec, ThumbnailingError

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ConditionalInclusion

#excluded?, #included?, #inclusion_matcher

Constructor Details

#initialize(global, source_image_name, specs, use_multipart_api, matcher) ⇒ Thumbnail

Returns a new instance of Thumbnail.



145
146
147
148
149
150
151
# File 'lib/httpimagestore/configuration/thumbnailer.rb', line 145

def initialize(global, source_image_name, specs, use_multipart_api, matcher)
  @global = global
  @source_image_name = source_image_name
  @specs = specs
  @use_multipart_api = use_multipart_api
  inclusion_matcher matcher
end

Class Method Details

.match(node) ⇒ Object



99
100
101
# File 'lib/httpimagestore/configuration/thumbnailer.rb', line 99

def self.match(node)
  node.name == 'thumbnail'
end

.parse(configuration, node) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
# File 'lib/httpimagestore/configuration/thumbnailer.rb', line 103

def self.parse(configuration, node)
  use_multipart_api = node.values.length == 1 ? true : false

  nodes = use_multipart_api ?  node.children : [node]
  source_image_name = use_multipart_api ? node.grab_values('source image name').first : nil # parsed later

  nodes.empty? and raise NoValueError.new(node, 'thumbnail image name')
  matcher = nil

  specs = nodes.map do |node|
    if use_multipart_api
      image_name = node.grab_values('thumbnail image name').first
    else
      source_image_name, image_name = *node.grab_values('source image name', 'thumbnail image name')
    end

    operation, width, height, format, if_image_name_on, remaining = *node.grab_attributes_with_remaining('operation', 'width', 'height', 'format', 'if-image-name-on')

    matcher = InclusionMatcher.new(image_name, if_image_name_on) if if_image_name_on

    ThumbnailSpec.new(
      image_name,
      operation || 'fit',
      width || 'input',
      height || 'input',
      format || 'input',
      remaining || {},
      matcher
    )
  end

  matcher = InclusionMatcher.new(source_image_name, node.grab_attributes('if-image-name-on').first) if use_multipart_api

  configuration.processors << self.new(
    configuration.global,
    source_image_name,
    specs,
    use_multipart_api,
    matcher
  )
end

Instance Method Details

#realize(request_state) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/httpimagestore/configuration/thumbnailer.rb', line 153

def realize(request_state)
  client = @global.thumbnailer or fail 'thumbnailer configuration'

  rendered_specs = {}
  @specs.select do |spec|
    spec.included?(request_state)
  end.each do |spec|
    rendered_specs.merge! spec.render(request_state)
  end
  source_image = request_state.images[@source_image_name]

  thumbnails = {}
  input_mime_type = nil
  input_width = nil
  input_height = nil

  Thumbnail.stats.incr_total_thumbnail_requests
  Thumbnail.stats.incr_total_thumbnail_requests_bytes source_image.data.bytesize

  if @use_multipart_api
    log.info "thumbnailing '#{@source_image_name}' to multiple specs: #{rendered_specs}"

    # need to reference to local so they are available within thumbnail() block context
    source_image_name = @source_image_name
    logger = log

    begin
      thumbnails = client.with_headers(request_state.headers).thumbnail(source_image.data) do
        rendered_specs.each_pair do |name, spec|
          begin
            thumbnail(*spec)
          rescue HTTPThumbnailerClient::HTTPThumbnailerClientError => error
            logger.warn 'got thumbnailer error while passing specs', error
            raise ThumbnailingError.new(source_image_name, name, error)
          end
        end
      end
    rescue HTTPThumbnailerClient::HTTPThumbnailerClientError => error
      logger.warn 'got thumbnailer error while sending input data', error
      raise ThumbnailingError.new(source_image_name, nil, error)
    end

    input_mime_type = thumbnails.input_mime_type
    input_width = thumbnails.input_width
    input_height = thumbnails.input_height

    # check each thumbnail for errors
    thumbnails = Hash[rendered_specs.keys.zip(thumbnails)]
    thumbnails.each do |name, thumbnail|
      if thumbnail.kind_of? HTTPThumbnailerClient::HTTPThumbnailerClientError
        error = thumbnail
        log.warn 'got single thumbnail error', error
        raise ThumbnailingError.new(@source_image_name, name, error)
      end
    end

    # borrow from memory limit - note that we might have already used too much memory
    thumbnails.each do |name, thumbnail|
      request_state.memory_limit.borrow(thumbnail.data.bytesize, "thumbnail '#{name}'")
    end
  else
    name, rendered_spec = *rendered_specs.first
    log.info "thumbnailing '#{@source_image_name}' to '#{name}' with spec: #{rendered_spec}"

    begin
      thumbnail = client.with_headers(request_state.headers).thumbnail(source_image.data, *rendered_spec)
      request_state.memory_limit.borrow(thumbnail.data.bytesize, "thumbnail '#{name}'")

      input_mime_type = thumbnail.input_mime_type
      input_width = thumbnail.input_width
      input_height = thumbnail.input_height

      thumbnails[name] = thumbnail
    rescue HTTPThumbnailerClient::HTTPThumbnailerClientError => error
      log.warn 'got thumbnailer error', error
      raise ThumbnailingError.new(@source_image_name, name, error)
    end
  end

  # copy input source path and url
  thumbnails.each do |name, thumbnail|
    thumbnail.extend 
    thumbnail.source_path = source_image.source_path
    thumbnail.source_url = source_image.source_url

    Thumbnail.stats.incr_total_thumbnail_thumbnails
    Thumbnail.stats.incr_total_thumbnail_thumbnails_bytes thumbnail.data.bytesize
  end

  # use httpthumbnailer provided information on input image mime type and size
  source_image.mime_type = input_mime_type if input_mime_type
  source_image.width = input_width if input_width
  source_image.height = input_height if input_height

  request_state.images.merge! thumbnails
end