Using Carrierwave with Cloudfront - ruby-on-rails

I'm using CarrierWave for images and Amazon Cloudfront as a CDN (without S3).
The issue is that something like: #user.image_url returns the non CDN URL, even though i've configured my assets accordingly:
# /config/environments/production.rb
config.action_controller.asset_host = Proc.new { |source, request|
if ['jpg','jpeg','png','gif','bmp'].include?(source.split('.').last)
unless request.ssl?
"http://cdn.domain.com"
else
"https://ge95v2x8h9t3.cloudfront.net"
end
end
}
How to make CarrierWave use my asset_host proc just like other assets?

You can configure carrierwave to use a custom asset_host (config.fog_host... documented in the readme). Although not documented, you can also use a Proc - or anything responding to :call - to determine the string at runtime:
https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/storage/fog.rb#L107
I'm not sure of a way to just point config.fog_host directly to Rails' config.asset_host, but I'm sure there must be a way to get a reference to it - even if you have to use a non-public interface. Though, I don't know how helpful that would be during development... you likely want assets served from localhost and uploads served from Cloudfront.

Related

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?

conditional configuration of config.action_controller.asset_host

I have a Rails app which uses CloudFront to speed up asset delivery, all is nice and smooth, have this added under production.rb
config.action_controller.asset_host = "http://cdn.mydomain.com/"
However, a bunch of clients on a restricted network use to go through a Varnish cache to hit my app, and cannot resolve the CDN domain. Varnish adds this header:
request.env["HTTP_X_VARNISH"]
What I'd like to do is basically to only use the CDN when this header is not set (clients not coming through varnish).
So basically something like
config.action_controller.asset_host = "http://cdn.mydomain.com/" unless request.env["HTTP_X_VARNISH"]
How could this be accomplished?
TIA
asset_host can be a Proc.
config.action_controller.asset_host = ->(source, request) do
if request.env["HTTP_X_VARNISH"]
"#{request.protocol}#{request.host_with_port}"
else
"http://cdn.mydomain.com/"
end
end

Issue with using CDN and https: (Rails)

In my Rails application, i have used CDN. I have configured the cdn by adding cdn url to
config.action_controller.asset_host = "http://cdn.mydomain.com"
in production.rb file.
Now i am trying to have https:// for certain pages like Sign In and Sign Up
But as the assets are served from CDN, the https conflicts with the cdn path.
My solution to this to make the sign in and sign up pages not to use the cdn assets and should point
as local assets.
is my solution correct? if so how do i restrict certain layout files from using CDN asset path?
I would look at this response: Configure dynamic assets_host in Rails 3
What I think you would want to do is change asset_host to be dynamic based on whether your page is served over https or not. Something like:
config.action_controller.asset_host = Proc.new { |source, request|
"#{request.ssl? ? '/assets' : 'http://cdn.mydomain.com'}"
}
My syntax may be a little off as I'm typing it up on the fly but it should be close to what you need.
NOTE : The code request.try(:ssl?) always returns false even when i run the https version.
I am working on finding the solution, will post it once i find it.
Found the solution
config.action_controller.asset_host = Proc.new do |*args|
source, request = args
if request.try(:ssl?)
'https://mydomain.com'
else
'http://cdn.mydomain.com'
end
end

Switch asset host for controller

Trying to figure out a way to change up the asset host when accessed by a certain controller.
The controller is to be strictly accessed by the https protocol, so I need the asset host to be switched over to using https. At the moment the asset host is set to a CNAME subdomain that is linked to the S3 and there is no SSL cert associated to it. What I'm trying to achieve is replace the current asset host with the https Amazon S3 URL. The only assets I'm worried about are the CSS and JS includes.
I was thinking of using a helper to strip the host from the stylesheet_link_tag and javascript_include_tag and replace them with the https Amazon S3 url. Seems a bit hackish to me though.
Or perhaps there is a way to changed asset hosts if request.ssl? is true?
I'm using Rails 3.2.x.
Figure out a solution for my case.
Ended up using a Proc on config.action_controller.action_host in my Production environment file to handle a logic on request.ssl? and respond accordingly. Here is the code
config.action_controller.asset_host = Proc.new { |source, request = nil, *_|
request && request.ssl? ? 'https://s3.amazonaws.com/my_bucket' : 'http://s3.my-domain.com'
}
'request' is set to nil to accomodate the cases where asset_host is called in asset files (CSS and JS if you are using the asset helper tags). Since request doesn't exist and if request isn't assigned in the args, then the error will be thrown when assets are compiled (as shown below).
This asset host cannot be computed without a request in scope. Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.
The *_ is present due to a bug with option arguments in Proc http://bugs.ruby-lang.org/issues/5694

Rails: Images on one server, CSS and Javascript on another

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.

Resources