# frozen_string_literal: true module DynamicImage # = DynamicImage Helper # # Provides helper methods for rendering and linking to images. module Helper # Returns the path for a DynamicImage::Model record. # Takes the same options as +dynamic_image_url+ def dynamic_image_path(record_or_array, options = {}) dynamic_image_url(record_or_array, { routing_type: :path }.merge(options)) end # Returns an HTML image tag for the record. If no size is given, it will # render at the original size. # # ==== Options # * :alt: If no alt text is given, it will default to the # filename of the uploaded image. # # See +dynamic_image_url+ for info on how to size and cropping. Options # supported by +polymorphic_url+ will be passed to the router. Any other # options will be added as HTML attributes. # # ==== Examples # # image = Image.find(params[:id]) # dynamic_image_tag(image) # # => # dynamic_image_tag(image, size: "100x100", alt="Avatar") # # => Avatar def dynamic_image_tag(record_or_array, options = {}) size = fit_size!(record_or_array, options) url_options = options.extract!(*allowed_dynamic_image_url_options) html_options = { size: }.merge(options) image_tag(dynamic_image_path_with_size(record_or_array, size, url_options), html_options) end # Returns the URL for a DynamicImage::Model record. # # ==== Options # # * :size - Desired image size, supplied as "{width}x{height}". # The image will be scaled to fit. A partial size like "100x" or "x100" # can be given, if you want a fixed width or height. # * :crop - If true, the image will be cropped to the given size. # * :upscale - By default, DynamicImage only scale images down, # never up. Pass upscale: true to force upscaling. # # Any options supported by +polymorphic_url+ are also accepted. # # ==== Examples # # image = Image.find(params[:id]) # dynamic_image_url(image) # # => "http://example.com/images/96...d1/300x187/1-2014062020...00.jpg" # dynamic_image_url(image, size: '100x100') # # => "http://example.com/images/72...c2/100x62/1-2014062020...00.jpg" # dynamic_image_url(image, size: '100x100', crop: true) # # => "http://example.com/images/a4...6b/100x100/1-2014062020...00.jpg" def dynamic_image_url(record_or_array, options = {}) size = fit_size!(record_or_array, options) dynamic_image_url_with_size(record_or_array, size, options) end # Returns a path to the original uploaded file for download, # without any processing applied. Sizing options are not # supported. def download_dynamic_image_path(record_or_array, options = {}) dynamic_image_path(record_or_array, { action: :download }.merge(options)) end # Returns a URL to the original uploaded file for download, # without any processing applied. Sizing options are not # supported. def download_dynamic_image_url(record_or_array, options = {}) dynamic_image_url(record_or_array, { action: :download }.merge(options)) end # Returns a path to the original uploaded file, without any processing # applied. Sizing options are not supported. def original_dynamic_image_path(record_or_array, options = {}) dynamic_image_path(record_or_array, { action: :original }.merge(options)) end # Returns a URL to the original uploaded file, without any processing # applied. Sizing options are not supported. def original_dynamic_image_url(record_or_array, options = {}) dynamic_image_url(record_or_array, { action: :original }.merge(options)) end # Same as +dynamic_image_path+, but points to an image with any # pre-cropping disabled. def uncropped_dynamic_image_path(record_or_array, options = {}) dynamic_image_path(record_or_array, { action: :uncropped }.merge(options)) end # Same as +dynamic_image_tag+, but renders an image with any # pre-cropping disabled. def uncropped_dynamic_image_tag(record_or_array, options = {}) dynamic_image_tag(record_or_array, { action: :uncropped }.merge(options)) end # Same as +dynamic_image_url+, but points to an image with any # pre-cropping disabled. def uncropped_dynamic_image_url(record_or_array, options = {}) dynamic_image_url(record_or_array, { action: :uncropped }.merge(options)) end private def allowed_dynamic_image_url_options %i[format only_path protocol host subdomain domain tld_length port anchor trailing_slash script_name action routing_type ] end def default_format_for_image(record) Mime::Type.lookup(record.safe_content_type).to_sym end def dynamic_image_digest(record, action, size = nil) key = [action || "show", record.id, size].compact.join("-") DynamicImage.digest_verifier.generate(key) end def dynamic_image_path_with_size(record_or_array, size = nil, options = {}) dynamic_image_url_with_size(record_or_array, size, { routing_type: :path }.merge(options)) end def dynamic_image_url_with_size(record_or_array, size = nil, options = {}) record = extract_dynamic_image_record(record_or_array) options = { routing_type: :url, action: nil, format: default_format_for_image(record), size: }.merge(options) options[:digest] = dynamic_image_digest(record, options[:action], options[:size]) polymorphic_url(record_or_array, options) end def extract_dynamic_image_record(record_or_array) case record_or_array when Array record_or_array.last else record_or_array end end def filename_to_alt(str) File.basename(str, ".*") .sub(/-[[:xdigit:]]{32,64}\z/, "") .tr("-_", " ") .capitalize end def fit_size!(record_or_array, options) record = extract_dynamic_image_record(record_or_array) action = options[:action].try(:to_s) size_opts = options.extract!(:size, :crop, :upscale) if size_opts[:size] image_sizing(record, size_opts, (action == "uncropped")) else (action == "original" ? record.real_size : record.size).floor.to_s end end def image_sizing(record, size_opts, uncropped) ImageSizing .new(record, uncropped:) .fit(size_opts[:size], size_opts).floor.to_s end end end