Rails: Images on one server, CSS and Javascript on another - ruby-on-rails

I am working on a rails app that has a bunch (hundreds) of images that are hosted on an S3 server. To have helpers like image_tag point here I had to add this to by config/environments/development.rb test.rb and production.rb:
config.action_controller.asset_host = "http://mybucket.s3.amazonaws.com"
However, this also means that it looks there for CSS and Javascript. This is a huge pain because each time I change the CSS I have to re-upload it to Amazon.
So.. Is there an easy way I can make my app look to Amazon for images, but locally for CSS/Javascript?
(I'm using Rails 3.0)

You can pass a Proc object to config.action_controller.asset_host and have it determine the result programmatically at runtime.
config.action_controller.asset_host = Proc.new do |source|
case source
when /^\/(images|videos|audios)/
"http://mybucket.s3.amazonaws.com"
else
"http://mydomain.com"
end
end
but left as it is, this would give you http://mybucket.s3.amazonaws.com/images/whatever.png when you use image_tag :whatever.
If you want to modify the path as well, you can do something very similar with config.action_controller.asset_path
config.action_controller.asset_path = Proc.new do |path|
path.sub /^\/(images|videos|audios)/, ""
end
which would give you http://mybucket.s3.amazonaws.com/whatever.png combined with the former.

There's nothing stopping you from passing full url to image_tag: image_tag("#{IMAGE_ROOT}/icon.png").
But to me moving static images (icons, backgrounds, etc) to S3 and leaving stylesheets/js files on rails sounds kinda inconsistent. You could either move them all to S3 or setup Apache for caching (if you're afraid users pulling big images will create too much overhead for Rails).
BTW, you don't have to put config.action_controller... into config files for all three environments: placing that line just in config/environment.rb will have the same effect.

Related

Rails + S3: Parse bucket and load images outside of controller

In a controller in my Rails app I had a function that parses my S3 bucket and selects images. It's causing page load speeds to go slow but I like being able to loop through the bucket without having all the URLs hard coded.
Here is what I have:
#bucket = S3_BUCKET
#images = []
#bucket.objects.each do |file|
if file.key.include?("inspiration")
#images << { url: file.public_url, key: file.key, type: 'file'}
end
end
Is there another way to accomplish this so page load speeds don't suffer?
As it turns out there were many more files than expected and the loop took a long time to complete. I changed the code to:
#images = #bucket.objects({prefix: 'inspiration')
and the response was much faster.
Since you really can't regulate the speed at which you access your s3 bucket I would suggest setting up a CDN(Content delivery network) on Amazons Cloudfont. Please take a look at this article Written by Brandon Hikert about implementing a CDN
https://brandonhilkert.com/blog/setting-up-a-cloudfront-cdn-for-rails/
Side note - If you would like a free CDN option I would use
https://cloudinary.com/pricing
Referencing when to use a CDN over s3
https://stackoverflow.com/questions/3327425/when-to-use-amazon-cloudfront-or-s3

Rails Sprockets file uses local manifest for digests instead of asset host version

In our web application built in Rails we have several clients using the same application who will have different assets that are used dependant on which subdomain is used.
To achieve this we swap out what folder is being used on the CDN like so:
config.action_controller.asset_host = Proc.new { |source, request|
if request.subdomain.present?
"http#{request.ssl? ? 's' : ''}://cdn.domain.com/#{request.subdomain}/"
else
"http#{request.ssl? ? 's' : ''}://#{request.host_with_port}/"
end
}
Each time we create a new client we compile the assets manually using a custom build tool that uses Sprockets to build the assets the same way Rails would and then upload them to our CDN under a folder that matches the subdomain. This then allows us to have different sets of assets based purely on the subdomain.
Now this works fine except that when we update an asset the digest will change for that file but Rails will still try and load the old asset digests because the sprockets-manifest file (which is in /public/assets) e.g. .sprockets-manifest-12345.json is being loaded instead of the one that's on the CDN. Even though the asset host is different it still loads the local one.
Rails it seems doesn't care about other manifest files as the file itself only stores the filename to the fingerprinted version so even when things like the host changes it would normally be able to find the correct asset. It would seem as though Rails has been designed this way deliberately.
However we really need to get Rails to use the manifest file that is on the CDN itself rather than use the one in the public folder local to the application.
After reading the docs, it seems you can change the manifest location. We tried doing it by using the same logic as above for the manifest like so:
config.assets.manifest = Proc.new { |source, request|
if request.subdomain.present?
"http#{request.ssl? ? 's' : ''}://cdn.domain.com/#{request.subdomain}/"
else
"http#{request.ssl? ? 's' : ''}://#{request.host_with_port}/"
end
}
But Rails/Sprockets is still using the local sprockets file... Any ideas why?

Turning off storage in Paperclip

How do I turn remote storage off in Paperclip for use on Heroku? I realize that storing an uploaded file is the whole point of this gem, but I want to turn it off and still use the gem's other features (inspecting the file, etc- just don't need to store). I want to keep all the functionality in place in the model, but just not store the file anywhere.
This is close but it doesn't work on Heroku:
Paperclip::Attachment.default_options[:storage] = 'filesystem'
This doesn't work unfortunately:
Paperclip::Attachment.default_options[:storage] = :none
From this file
https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/railtie.rb
I did not tested this but inside config/initializer/ or production.rb environment you should be able to set
Paperclip::Attachment.default_options = {}
or with Paperclip::Attachment.default_options[:storage] = ""
Hope it helps

Rails asset pipeline doesn't serve images

My Rails app doesn't serve images at all.
image_url('picture.jpg')
# will result in url(http://localhost:3000/images/picture.jpg)
# but should be url(http://localhost:3000/assets/picture.jpg)
image_tag 'picture.jpg'
asset_url 'picture.jpg'
# will result in the same url / path as image_url()
Neither http://localhost:3000/images/picture.jpg nor http://localhost:3000/assets/picture.jpg exists, while http://localhost:3000/assets/images/picture.jpg does.
Here is a gist of my application.rb and development.rb: https://gist.github.com/maximski/1ccb75f6f89c02932239
I am in development environment and I don't want to precompile files manually. The app is pretty much much newly generated so the configuration is almost completely set on default.
This problem appear if images doesn't exists in app/assets/images directory. Check that app/assets/images/picture.jpg file is exists.

Disable Rails 3.1 asset pipeline for images only?

I'm upgrading a Rails 3 app to 3.2 and setting up the asset pipeline. It's great for css/js but I don't really see the point in using it for images, and unfortunately I have css with a ton of references /images/*.png and the like.
Is there a way to disable the asset pipeline just for images so image_tag("x.png") will go back to returning <img src="/images/x.png"> instead of <img src="/assets/x.png">? Thanks!
You can monkey-patch ActionView::Base, try this in rails console:
helper.image_path "foo" #=> "/assets/foo"
module OldImagePath
def image_path(source)
asset_paths.compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path
end
ActionView::Base.send :include, OldImagePath
helper.image_path "foo" #=> "/images/foo"
You can place this in an initializer for example. By default ActionView::Base includes ActionView::Helpers::AssetTagHelper and Sprockets::Helpers::RailsHelper which both define image_path but the latter take precedence. I'm including my own module which take precedence over all of them (the code inside is taken from ActionView::Helpers::AssetTagHelper).
Although, it makes sense to use asset pipeline for images too. They get hash sum in their filenames so that they can be cached forever on the client side without asking the server whether the file was changed.
Have a look at this gem:
https://github.com/spohlenz/digestion, it should do what you need :).
Otherwise you can move the assets you don't want included in the asset pipeline from app/assets back into public (e.g. public/images). Everything should work as you expect without the need for a gem.

Resources