38

I have an Image, which contains carrierwave uploads:

Image.find(:first).image.url #=> "/uploads/image/4d90/display_foo.jpg"

In my view, I want to find the absolute url for this. Appending the root_url results in a double /.

root_url + image.url #=> http://localhost:3000//uploads/image/4d90/display_foo.jpg

I cannot use url_for (that I know of), because that either allows passing a path, or a list of options to identify the resource and the :only_path option. Since I do't have a resource that can be identified trough "controller"+"action" I cannot use the :only_path option.

url_for(image.url, :only_path => true) #=> wrong amount of parameters, 2 for 1

What would be the cleanest and best way to create a path into a full url in Rails3?

Community
  • 1
  • 1
berkes
  • 26,996
  • 27
  • 115
  • 206

9 Answers9

99

You can also set CarrierWave's asset_host config setting like this:

# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.storage = :file
  config.asset_host = ActionController::Base.asset_host
end

This ^ tells CarrierWave to use your app's config.action_controller.asset_host setting, which can be defined in one of your config/envrionments/[environment].rb files. See here for more info.

Or set it explicitly:

  config.asset_host = 'http://example.com'

Restart your app, and you're good to go - no helper methods required.

* I'm using Rails 3.2 and CarrierWave 0.7.1

ncherro
  • 2,604
  • 2
  • 24
  • 20
33

try path method

Image.find(:first).image.path

UPD

request.host + Image.find(:first).image.url

and you can wrap it as a helper to DRY it forever

request.protocol + request.host_with_port + Image.find(:first).image.url
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • That returns the entire (system) path. `=> "/home/..../public/uploads/image/4d90/foo.jpg"` I need the URL, not the path, for use in an atom feed. – berkes Mar 30 '11 at 10:04
  • oh yes. then try `request.host + Image.find(:first).image.url` – fl00r Mar 30 '11 at 10:14
  • 1
    Also not :( `"localhost/uploads/image/4d90/display_foo.jpg"` It misses the :3000-port (on development) and the http(s)://. – berkes Mar 30 '11 at 10:19
  • 2
    `request.protocol + request.host_with_port + Image.find(:first).image.url` – fl00r Mar 30 '11 at 10:27
  • 2
    I am using delayed job to queue emailing jobs. The jobs renders emailing templates. However at this time the request will be nil (it is no longer part of an HTTP request as per se). – lulalala Dec 22 '11 at 10:11
11

Another simple method to use is URI.parse, in your case would be

require 'uri'

(URI.parse(root_url) + image.url).to_s

and some examples:

1.9.2p320 :001 > require 'uri'
 => true 
1.9.2p320 :002 > a = "http://asdf.com/hello"
 => "http://asdf.com/hello" 
1.9.2p320 :003 > b = "/world/hello"
 => "/world/hello" 
1.9.2p320 :004 > c = "world"
 => "world" 
1.9.2p320 :005 > d = "http://asdf.com/ccc/bbb"
 => "http://asdf.com/ccc/bbb" 
1.9.2p320 :006 > e = "http://newurl.com"
 => "http://newurl.com" 
1.9.2p320 :007 > (URI.parse(a)+b).to_s
 => "http://asdf.com/world/hello" 
1.9.2p320 :008 > (URI.parse(a)+c).to_s
 => "http://asdf.com/world" 
1.9.2p320 :009 > (URI.parse(a)+d).to_s
 => "http://asdf.com/ccc/bbb" 
1.9.2p320 :010 > (URI.parse(a)+e).to_s
 => "http://newurl.com" 
c2h2
  • 11,911
  • 13
  • 48
  • 60
5

Just taking floor's answer and providing the helper:

# Use with the same arguments as image_tag. Returns the same, except including
# a full path in the src URL. Useful for templates that will be rendered into
# emails etc.
def absolute_image_tag(*args)
  raw(image_tag(*args).sub /src="(.*?)"/, "src=\"#{request.protocol}#{request.host_with_port}" + '\1"')
end
Martin T.
  • 3,132
  • 1
  • 30
  • 31
  • 2
    This helper works great within standard views, but you cannot use this in Mailer views, as Mailers have no sense of a "request", so your helper will fail. – Eric Jan 21 '12 at 21:15
  • True. I'm not sure, but I think with Rails 3.1 and the asset pipeline, you could use the asset host to put together full URLs. With earlier versions, I think one would need to hardcode the host. – Martin T. Jan 23 '12 at 09:19
  • @M.G.Palmer would this still work since carrierwave uploaded images reside inside the public folder? – lulalala Mar 16 '12 at 03:25
  • @lulalala I should think so, maybe you'll need to call with something like absolute_image_tag("/../#{filename}") to get "up" from the default /images path. But I never used carrierwave so YMMV. – Martin T. Mar 16 '12 at 08:11
2

There's quite a bunch of answers here. However, I didn't like any of them since all of them rely on me to remember to explicitly add the port, protocol etc. I find this to be the most elegant way of doing this:

full_url = URI( root_url )
full_url.path = Image.first.image.url
# Or maybe you want a link to some asset, like I did:
# full_url.path = image_path("whatevar.jpg")
full_url.to_s

And what is the best thing about it is that we can easily change just one thing and no matter what thing that might be you always do it the same way. Say if you wanted to drop the protocol and and use the The Protocol-relative URL, do this before the final conversion to string.

full_url.scheme = nil

Yay, now I have a way of converting my asset image urls to protocol relative urls that I can use on a code snippet that others might want to add on their site and they'll work regardless of the protocol they use on their site (providing that your site supports either protocol).

Timo
  • 3,335
  • 30
  • 25
2

I used default_url_options, because request is not available in mailer and avoided duplicating hostname in config.action_controller.asset_host if haven't specified it before.

config.asset_host = ActionDispatch::Http::URL.url_for(ActionMailer::Base.default_url_options)
Eugene
  • 991
  • 10
  • 10
1

You can't refer to request object in an email, so how about:

def image_url(*args)
    raw(image_tag(*args).sub /src="(.*?)"/, "src=\"//#{ActionMailer::Base.default_url_options[:protocol]}#{ActionMailer::Base.default_url_options[:host]}" + '\1"')
end
Luke W
  • 8,276
  • 5
  • 44
  • 36
1

You can actually easily get this done by

root_url[0..-2] + image.url

I agree it doesn't look too good, but gets the job done.. :)

Alfie
  • 2,706
  • 1
  • 14
  • 28
1

I found this trick to avoid double slash:

URI.join(root_url, image.url)
Artem P
  • 5,198
  • 5
  • 40
  • 44