Colin Law wrote:
open("http://chart.apis.google.com/chart#\{CGI\.escape\('?cht=bvg&chbh=a&chd=s:vttusty&chs=500x300&chxt=x,y&chxl=0:|Sun|Mon|Tue>Wed>Thu>Fri>Sat>1:|0|2|4|6|8|10|12'\)\}"\)
do |chart|
File.open('chart.png', 'w') {|f| f.write chart.read }
end
I definitely understand the why you would want to escape the URI, but
I'm not convinced that this approach will never work.
The URL with the escaped characters causes Google to reject it as
invalid parameters, which is why you got all that HTML and Javascript.
E.g. if you use your browser to open the result of the above URI with
escaped characters:
http://chart.apis.google.com/chart%3Fcht%3Dbvg%26chbh%3Da%26chd%3Ds%3Avttusty%26chs%3D500x300%26chxt%3Dx%2Cy%26chxl%3D0%3A|Sun|Mon|Tue|Wed|Thu|Fri|Sat|1%3A|0|2|4|6|8|10|12
You are redirected to the Google chart API. Retaining the original
characters with:
http://chart.apis.google.com/chart?cht=bvg&chbh=a&chd=s:vttusty&chs=500x300&chxt=x,y&chxl=0:|Sun|Mon|Tue|Wed|Thu|Fri|Sat|1:|0|2|4|6|8|10|12
you'll get a PNG image and nothing else.
Okay, so Google doesn't want the escaped characters, and the URI::open
method doesn't want the plain characters... or does it? Is there no way
around this? The thing is the characters used in the URL above are
valid, or so I thought. I pass these kind of characters as parameters
from controller to view all throughout my application, with full browser
compatibility.
I tried using backslashes for all the characters, e.g. "\&" instead of
"&" and the URI open method still rejects it.
I've seen some people successfully get images to save to the file system
using URI, but I'm assuming all their code is outdated as I could not
get it to work. Also, the image URL they were using actually pointed to
a PNG image, with a PNG extension, whereas the Google Chart URL will
return a PNG image.
I appreciate all the help!
You just need a URI that's technically valid, not just one that browsers understand. URI.parse and thus open-uri is rather more strict than the other tools suggested.
Here's an irb session that shows the right way to get a valid URI for the chart you want.
$ irb -ruri -ropen-uri -rcgi
orig = 'http://chart.apis.google.com/chart?cht=bvg&chbh=a&chd=s:vttusty&chs=500x300&chxt=x,y&chxl=0
:|Sun|Mon|Tue|Wed|Thu|Fri|Sat|1:|0|2|4|6|8|10|12'
=> "http://chart.apis.google.com/chart?cht=bvg&chbh=a&chd=s:vttusty&chs=500x300&chxt=x,y&chxl=0:|Sun|Mon|Tue|Wed|Thu|Fri|Sat|1:|0|2|4|6|8|10|12"
md = %r{(https?:)//((?i:[-a-z0-9.]+))(/[^?]+)\??(.*)}.match(orig)
=> #<MatchData:0x391070>
scheme, host, path, query_string = md.captures
=> ["http:", "chart.apis.google.com", "/chart", "cht=bvg&chbh=a&chd=s:vttusty&chs=500x300&chxt=x,y&chxl=0:|Sun|Mon|Tue|Wed>Thu>Fri>Sat>1:|0|2|4|6|8|10|12"]
query_string.split('&').map{|pair| pair.split('=')}
=> [["cht", "bvg"], ["chbh", "a"], ["chd", "s:vttusty"], ["chs", "500x300"], ["chxt", "x,y"], ["chxl", "0:|Sun|Mon|Tue|Wed|Thu|Fri|Sat|1:|0|2|4|6|8|10|12"]]
u = URI.parse "#{scheme}//#{host}"
=> #<URI::HTTP:0x37cf44 URL:http://chart.apis.google.com>
u.path = path
=> "/chart"
u.query = query_string.split('&').map{|pair|
pair.split('=')}.map{|n,v| "#{n}=#{CGI.escape(v)}"}.join('&')
=> "cht=bvg&chbh=a&chd=s%3Avttusty&chs=500x300&chxt=x%2Cy&chxl=0%3A%7CSun%7CMon%7CTue%7CWed%7CThu%7CFri%7CSat%7C1%3A%7C0%7C2%7C4%7C6%7C8%7C10%7C12"
open(u.to_s) do |chart|
?> File.open('chart.png', 'w') {|f| f.write chart.read }
end
=> 5445
File.exist?('chart.png')
=> true
Note that if you are building up the URI yourself, you could just create it with valid escaping on the query string rather than pulling it apart and escaping it as you put it back together.
-Rob
Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com