I have found it useful to populate my local development database with data from our production server in order to be able to get good test coverage. However, a perpetual problem I’ve had with this approach is that it introduces an environment where sometimes images are available and sometimes they aren’t (the database knows about all the images, but some were uploaded locally, some reside on our main servers, and some are on S3).
What I’ve found is that even though Rails doesn’t give exceptions when it finds missing images, it does start to get painfully slow. Each missing image it has to process usually takes about 2 seconds. On pages with 5-10 missing images, the wait could be quite painful.
So I finally got fed up yesterday and wrote a hacky patch to get around this problem. Here it is:
def self.force_image_exists(image_location)
default_image = "/images/dumpster.gif"
if(image_location && (image_location.index("http") || File.exists?(RAILS_ROOT + "/public" + image_location.gsub(/\?.*/, ''))))
image_location
else
default_image
end
end
This function is part of a utility class (named “UtilityGeneral”) that we use for various miscellaneous tasks. I call this method from a simple mixin:
if RAILS_ENV == 'development'
module ActionView
module Helpers #:nodoc:
module AssetTagHelper
# replace image tag
def path_to_image(source)
original_tag = ImageTag.new(self, @controller, source).public_path
UtilityGeneral.force_image_exists(original_tag)
end
end
end
end
end
If anyone else works locally with images that may or may not exist, this wee patch should come in handy to save you from load times of doom on pages that are missing images. It just subs in an alternate image when the real image doesn’t exist locally.
P.S. When I grow up, I want a blog about coding that lets me paste code.
P.S.S. 4/10: I grew up!
Great tip. I adjusted it so you can just stick it in a file in your initializers folder:
http://snipt.net/benr75/suppress-missing-images-in-development-mode-place-in-rb-file-in-your-initializers-folder
Hey Ben,
Good idea, but I think there are a couple typos in the example. From what I can tell, it looks like the snippet would use the trash_can image every time, rather than only using it when original_tag can’t be found.
Also, default_image (the else case) isn’t declared in the snippet.
I do this using mod_rewrite, which should be a bit quicker. If a path looks like an image, and it’s not found, then rewrite it to a default image:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _small.(jpg|png)$ /images/photo_unavailable_small.png [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _large.(jpg|png)$ /images/photo_unavailable_large.png [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/images/products /images/photo_unavailable_large.png [L]
These rules could definitely be more efficient – there’s no need to do the file exists test in every case, and so often!
This is my example:
ServerName localhost
ServerAlias modcloth
AllowEncodedSlashes on # 404-not found error on a page wherein which the URL has %2F and it needs to be decoded as /
# Fake productshots to avoid slow 404 on every product image.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _carousel.(jpg|png)$ /home/mario/public_html/fake_productshots/PRODUCT_carousel.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _category.(jpg|png)$ /home/mario/public_html/fake_productshots/PRODUCT_category.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _quicklook.(jpg|png)$ /home/mario/public_html/fake_productshots/PRODUCT_quicklook.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _thumb.(jpg|png)$ /home/mario/public_html/fake_productshots/PRODUCT_thumb.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule _tiny.(jpg|png)$ /home/mario/public_html/fake_productshots/PRODUCT_tiny.jpg [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .(jpg|png)$ /var/www/home/mario/public_html/fake_productshots/PRODUCT.jpg [L]
# To use Rails Mongrel cluster as proxy
ProxyPass / balancer://mongrelcluster/
ProxyPassReverse / balancer://mongrelcluster/
ProxyPreserveHost on
If anyone’s trying to make this work under Rails 2.3, here’s what I did, put everything in an Initializer :
# originally found at https://www.williambharding.com/blog/rails/rails-fix-slow-loads-in-development-when-images-missing/
if RAILS_ENV == ‘development’
module ActionView
module Helpers #:nodoc:
module AssetTagHelper
def path_to_image(source)
original_tag = compute_public_path(source, “images”)
# Create the file below or simply point it to your own placeholder
default_image = “/images/404_dev_image.jpg”
if(original_tag && (original_tag.index(“http”) || File.exists?(RAILS_ROOT + “/public” + original_tag.gsub(/?.*/, ”))))
original_tag
else
default_image
end
end
end
end
end
end