Full url for an image-path in Rails 3 - ruby-on-rails

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?

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

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

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"

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

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).

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)

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

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.. :)

I found this trick to avoid double slash:
URI.join(root_url, image.url)

Related

rails saving recognized link from text to database with rinku

I have a rails app with chat. In the chat for the messages I use rinku gem to recognize links which works well. On the top of this I would like to save the links as message.link without the rest of the text around it from the message.body.
So for example in the code below the user sent the message.body "hi there www.stackoverflow.com" and I would like to save only the "www.stackoverflow.com" as message.link. How can I do that?
view
<p><%= find_links(message.body) %></p>
controller
def find_links(message_body)
Rinku.auto_link(message_body, mode=:all, 'target="_blank"', skip_tags=nil).html_safe
end
it will appear in the DOM as:
<p>hey there http://stackoverflow.com/</p>
and will appear in the db as message.body:
"hey there http://stackoverflow.com/"
UPDATE:
messages controller
require "uri"
def create
.....
if message.save
link = URI.extract(message.body)
update_attribute(message.link = link)
end
You need regular expression to identify URLs from text. Try following regular expression:
/(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/
Working demo: http://rubular.com/r/bHQdFHZYFH
2.1.2 :001 > str = "hey hi hello www.google.com https://stackoverflow.com http://tech-brains.blogspot.in"
2.1.2 :002 > regexp = /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/
2.1.2 :003 > str.scan(regexp)
=> [["www.google.com"], ["https://stackoverflow.com"], ["http://tech-brains.blogspot.in"]]
You can use Ruby code:
2.0.0-p247 :001 > require "uri"
=> true
2.0.0-p247 :002 > URI.extract("hey there http://stackoverflow.com/")
=> ["http://stackoverflow.com/"]
Hope it helps!

Avoid I18n Fallback to default locale

I have the following configured in my application.rb
config.i18n.available_locales = [:at, :de, :ch_de, :ch_fr, :fr, :int_en, :int_fr]
config.i18n.default_locale = :at
My default locale is set to :at (Austria). Which I require for Route Translation. Rails server won't start without it and to be fair it makes sense.
I now created a fallback map, which works just fine:
config.i18n.fallbacks = {'de' => 'at', 'ch_de' => 'at', 'ch_fr' => 'fr', 'int_fr' => 'fr', 'fr' => 'fr', 'int_en' => 'int_en'}
So basically I want all German speaking countries to fallback on :at, whilst all French speaking countries fall back to :fr.
However, I do NOT under any circumstances want :fr to fallback on :at. This is for SEO purposes as some french pages do not have metadata configured. So now the french pages would display the Austrian :at metadata entry. Which is wrong.
If I turn of fallbacks completely:
config.i18n.fallbacks = false
the following works fine in my views:
t('.metatitle', :default => "")
In that case if there is no translation available then nothing is displayed. However, the rest of the site that already exists relies on fallbacks - so this is not an option, considering the effort to implement the change.
Is there a way turn off fallbacks for individual translations?
Or can I implement the fallback map and make sure that the map doesn't fallback to it's default locale if for example no french :fr translation exists?
PS: The route translating gem that requires the default locale is this one here.
Thank you for your help !
Figured it out - and thought of sharing it with you:
If you wish to avoid fallback to the default locale on individual translation you simply have to send a empty fallback array like this:
t('.metatitle', :default => "", :fallback => [])
Et Voila !
This is tricky in Rails up to 6.1 because you need to beat the logic in the Railtie initializer which desperately wants to fallback to the default_locale.
To set the default fallback locale to nil you need to use the following code:
config.i18n.default_locale = "de-AT"
config.i18n.fallbacks.defaults = [[]] # If you just write [], then default_locale is used
config.i18n.fallbacks.map = {
:de => "de-AT",
"de-CH" => "de-AT",
}
Let's check:
$ rails console
2.7.2 :001 > I18n.fallbacks["de"]
=> [:de, :"de-AT"]
2.7.2 :002 > I18n.fallbacks["fr"]
=> [:fr]
2.7.2 :003 > I18n.fallbacks["de-CH"]
=> [:"de-CH", :de, :"de-AT"]
2.7.2 :004 > I18n.fallbacks["de-AT"]
=> [:"de-AT", :de]
2.7.2 :005 >
Not 100% what you want, but there just seems to be no way to prevent the fallbacks to go from country specific locale to generic language locale, when fallbacks are enabled.
Note #1: Your locales are a bit non-standard. AFAIK there is no 'at' locale, but only "de-AT".
Note #2: Some more subtleties and notes in this answer.

Retrieve file from GridFS and pass as regular IO::File

I am storing, amongst others, Excel files using GridFs. I'd like to use the Spreadsheet gem to parse these.
I've tried this, but it (obviously!) did not work:
1.9.3p194 :036 > db = Mongo::Connection.new.db(Mongoid.database.name)
1.9.3p194 :037 > grid = Mongo::GridFileSystem.new(db)
1.9.3p194 :038 > f = grid.open('test1.xls', 'r')
=> #<GridIO _id: 500ef7cdc5ebb515c9000005>
1.9.3p194 :039 > Spreadsheet.open(f)
NoMethodError: undefined method `flush' for #<GridIO _id: 500ef7cdc5ebb515c9000005>
Would you have a good suggestion to 'transform' or 'wrap' the GridIO class into an IO::File -like instance so that I can pass the Excel file to the Spreadsheet open method.
The spreadsheet open method takes either an IO instance or a String specifying the path on disk (the latter not being useful when using GridFS):
(Object) open(io_or_path, mode = "rb+", &block)
Thanks!
It looks like this is a desired feature of the ruby driver that doesn't exist yet.
https://jira.mongodb.org/browse/RUBY-368
You could probably pass a block to Spreadsheet.open, as is suggested in the jira ticket:
db = Mongo::Connection.new.db(Mongoid.database.name)
Spreadsheet.open('filename', 'w') do |f|
gridfs = Mongo::GridFileSystem.new(db)
gridfs_file = gridfs.open('test1.xls', 'r')
f.write(gridfs_file.read()) until gridfs_file.eof?
end
Emily's pointer was quite helpful. For the time being, ended up using a temporary file:
Tempfile.open(["test", ".xls"]) do |fh|
gridfs = Mongo::GridFileSystem.new(Mongoid.database)
gridfs_file = gridfs.open('test1.xls', 'r')
fh.binmode
fh.write(gridfs_file.read)
#xls = Excel.new(fh)
fh.close
end

invalid URI - How to prevent, URI::InvalidURIError errors?

I got the following back from delayed_job:
[Worker(XXXXXX pid:3720)] Class#XXXXXXX failed with URI::InvalidURIError: bad URI(is not URI?): https://s3.amazonaws.com/cline-local-dev/2/attachments/542/original/mac-os-x[1].jpeg?AWSAccessKeyId=xxxxxxxx&Expires=1295403309&Signature=xxxxxxx%3D - 3 failed attempts
The way this URI comes from in my app is.
In my user_mailer I do:
#comment.attachments.each do |a|
attachments[a.attachment_file_name] = open(a.authenticated_url()) {|f| f.read }
end
Then in my attachments model:
def authenticated_url(style = nil, expires_in = 90.minutes)
AWS::S3::S3Object.url_for(attachment.path(style || attachment.default_style), attachment.bucket_name, :expires_in => expires_in, :use_ssl => attachment.s3_protocol == 'https')
end
That being said, is there some type of URI.encode or parsing I can do to prevent a valid URI (as I checked the URL works in my browser) for erroring and killing delayed_job in rails 3?
Thank you!
Ruby has (at least) two modules for dealing with URIs.
URI is part of the standard library.
Addressable::URI, is a separate gem, and more comprehensive, and claims to conform to the spec.
Parse a URL with either one, modify any parameters using the gem's methods, then convert it using to_s before passing it on, and you should be good to go.
I tried ' open( URI.parse(URI.encode( a.authenticated_url() )) ' but that errord with OpenURI::HTTPError: 403 Forbidden
If you navigated to that page via a browser and it succeeded, then later failed going to it directly via code, it's likely there is a cookie or session state that is missing. You might need to use something like Mechanize, which will maintain that state while allowing you to navigate through a site.
EDIT:
require 'addressable/uri'
url = 'http://www.example.com'
uri = Addressable::URI.parse(url)
uri.query_values = {
:foo => :bar,
:q => '"one two"'
}
uri.to_s # => "http://www.example.com?foo=bar&q=%22one%20two%22"

URL substring matching regular expression?

Suppose I only want user to type in url starts with http://www.google.com
What is the regular expression for this?
Thanks!
Just get the substring from 0 to the length of http://www.google.com and you're done.
Rather than use a regex, you might want to consider using the URI library that comes with Ruby. It's made to take apart and build URLs, is well tested, and less error-prone than trying to reinvent the same functionality.
require 'uri'
url = URI.parse('http://www.google.com/path/to/page.html?a=1&b=2')
url.scheme # => "http"
url.host # => "www.google.com"
url.path # => "/path/to/page.html"
url.query # => "a=1&b=2"
If that's not good enough, the Addressable::URI gem is even more capable.
Try this:
/\Ahttp:\/\/www\.google\.com(.*)?\Z/
ruby-1.9.2-p0 > "http://www.google.com" =~ /\Ahttp:\/\/www\.google\.com(.*)?\Z/
=> 0
ruby-1.9.2-p0 > "http://www.google.com/foobar" =~ /\Ahttp:\/\/www\.google\.com(.*)?\Z/
=> 0
ruby-1.9.2-p0 > $1
=> "/foobar"
Rails has a convenient start_with? method for this. If it's just a static string, no regular expression is needed.
url.start_with?("http://www.google.com")

Resources