Using Rails googlecharts gem on HTTPS/SSL site - ruby-on-rails

I am using the googlecharts gem in my rails app for some simple charting. It works beautifully, except my app is required to be SSL encrypted at all times. In order to pull the google charts, the charts gem of course makes an http request to google which leads to a browser warning about some insecure content on the page for most users. Has anyone else faced this issue and devised a solution to avoid the warning? I am afraid I will need to figure out a way to make the http call, store the image google returns locally, and then display that in the app but figured someone else has already found a nice way to handle this.

The API Google Charts API endpoint is stored in the class variable ##url inside the Gchart class. So initially I thought of monkeypatching the class variable to set the url to https
# Put this in an initializer
Gchart.send :class_variable_set, :##url, "https://chart.apis.google.com/chart?"
alas Google Charts does not work via https. So we can't use that method. As the Gchart class methods just return a URL we can wrap the calls up in a proxy controller method that does the API call server side and proxies it to the client via the ActionController send_data method using your protocol of choice. That way you don't have to reinvent the Gchart library wheel.
class ChartsController < ApplicationController
require 'net/http'
require 'gchart'
def show
options = params.except :controller, :action
options[:data].map! { |x| x.to_i } if options[:data]
begin
chart = URI.parse(Gchart.send options.delete(:type), options)
send_data Net::HTTP.get(chart), :content_type => 'image/png', :disposition => 'inline'
rescue
raise ActiveRecord::RecordNotFound
end
end
end
The helper you can use in your views:
module ApplicationHelper
def chart_tag(options ={})
image_tag chart_path(options)
end
end
and the route
map.resource :chart, :only => :show
Usage:
<%= chart_tag :type => "line", :size => '200x300', :title => "example title", :bg => 'efefef', :legend => ['first data set label', 'second data set label'], :data => [10, 30, 120, 45, 72] %>
Code is untested but should give you a good start.

Google charts supports now ssl :
use
https://chart.googleapis.com/chart
instead of :
http://chart.apis.google.com/chart

I'm using the GchartRB gem, and a modified version of the first solution worked for me as well. You'll have to use the to_escaped_url method for URI.parse to handle it correctly.

I don't know of an existing plugin that will do this, but you can do it on your own. Simply write a new controller method that will get the chart via HTTP and then return it immediately (no need to save it to a file)
In controller:
require 'net/http'
def googlechart
send_data Net::HTTP.get("http://chart.apis.google.com/chart?#{params[:api]}"),
:content_type => 'image/png',
:disposition => 'inline'
end
In view:
<%= image_tag googlechart_path(:api=>'cht=p&chd=s:Uf9a&chs=200x100&chl=January') %>
Just set up your route and you're all set.

Related

Paperclip image.url in Backbone.js Views

I am using paperclip to save images. Everything works fine and I am able to access the item's url with #item.image.url.
class Item
has_attached_file :image, :styles => {
:original => ['1920x1680>', :jpg],
:small => ['100x100>', :jpg],
:medium => ['250x250>', :jpg],
:large => ['500x500>', :jpg]
}
end
This is console:
> Item.last.image.url(:small)
=> "/system/images/items/1/small/chanel.jpg?1334005208"
This is straightforward and easy if I am templating HAML or ERB from the server and serving up the page to the user like this. items/show.html.haml:
.item
.item-image
= image_tag #item.image.url(:small)
However, with backbone.js, I am unable to construct the URL because I do not have the paperclip helpers in context. Essentially, I am sending the following attributes of the image to the page in json form.
#<Item:0x007fc97559b960> {
:id => 1,
:image_content_type => "image/jpeg",
:image_file_name => "chanel.jpg",
:image_file_size => 28880,
:image_updated_at => 2012-04-09 21:00:08 UTC
}
What is a ninja way to get the image.url included as an attribute on the item. How do I account for the style URLS? It would be nice to have an attribute like "image_small_url", "image_normal_url", etc predetermined and accessible. Thoughts?
I'm using Jbuilder to build the JSON views for a project I'm working on, so my index view, for example looks like this:
json.array!(#things) do |json, thing|
json.id thing.id
json.name thing.name
json.description thing.description
json.image_url thing.image.url
json.thumb_url thing.image.url(:thumb)
end
That way in my Backbone template, I can just say thing.get('image_url') and thing.get('thumb_url').
In brief, you'll want to use something like Jbuilder, or manually override as_json in your model. Personally, I like taking care of this at the view level, which is what Jbuilder allows you to do easily.
While generating JSON in controllers is doable, I greatly prefer wrapping up this kind of functionality in either decorators (e.g., Draper), or into frameworks designed for RESTful resources (e.g., Roar).
This keeps the mapping between models and their external representations highly localized, allows direct testing of representations outside of the web app framework, and so on.
For example, clem's answer would be isolated within a single class, roughly:
class ThingDecorator < Draper::Decorator
delegate_all
def image_url; object.image.url; end
def thumb_url; object.image.url(:thumb); end
end
Then in the controller, for example:
#things = Thing.some_scope.decorate # Or
#things = ThingDecorator.decorate_collection(Thing.all)
(Or whatever you need, and what works depends on the Rails version, see the Draper docs.)
Then expose the collection as JSON using normal means. IMO this is almost always cleaner.
Another simple example using jbuilder:
# index.json.jbuilder
json.array!(#shared_snap_casts) do |shared_snap_cast|
json.extract! shared_snap_cast, :id, :snap
end
Here the :snap is the paperclip attachment - the output from this example is:
[{"id":1,"snap":"/system/shared_snap_casts/snaps/000/000/001/original/some_icon.png?1388093936"}]
Hope this helps!

Showing images with carrierwave in rails 3.1 in a private store folder

I have a rails 3.1 app and I am adding carrierwave to store images. But I want to store those images outside the public folde,r because they should only be accessible when users are loaded in the app. So I changed the store_dir in carrerwave with the initializer file:
CarrierWave.configure do |config|
config.root = Rails.root
end
And my carrierwave uploader goes like this:
class ImageUploader < CarrierWave::Uploader::Base
...
def store_dir
"imagenes_expedientes/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
Images are stored correctly and if I use the public folder everything works fine. However when trying to move things to the private folder, images are not displayed and when I try to open them in a new window I get the following error:
Routing Error
No route matches [GET] "/imagenes_expedientes/volunteer/avatar/15/avatar.jpg"
I was trying to deliver the files using send_file through the controller, but instead of loading the page I only get the image downloaded.
def show
send_file "#{Rails.root}/imagenes_expedientes/avatar.jpg", :type=>"application/jpg", :x_sendfile=>true
end
Finally Images are displayed like this in the views:
<%= image_tag(#volunteer.avatar_url, :alt => "Avatar", :class => "avatar round") if #volunteer.avatar? %>
This may probably be solved rather easy, but since I am somehow new to Rails, I donĀ“t know what to do it. Should I set a route? Or is there anyway to display the images using the send_file method?
Thanks!
ANSWER
I managed to display images using x-sendfile and putting :disposition => 'inline' as suggested by clyfe. I made a new action in my controller:
def image
#volunteer = Volunteer.find(params[:id])
send_file "#{Rails.root}/imagenes_expedientes/#{#volunteer.avatar_url}",:disposition => 'inline', :type=>"application/jpg", :x_sendfile=>true
end
Added to the routes:
resources :volunteers do
member do
get 'image'
end
end
And displayed in the views:
<%= image_tag(image_volunteer_path(#volunteer), :alt => "Avatar", :class => "avatar round") if #volunteer.avatar? %>
Hope it helps others!
:disposition => 'inline' will make your images display in the browser instead of popping up the download dialog.
def show
send_file "#{Rails.root}/imagenes_expedientes/avatar.jpg", :disposition => 'inline', :type=>"application/jpg", :x_sendfile=>true
end
Depending on the images sizes and the number of users you might want to move this action to a metal app, or a separate so as to not block the server.

Download Image attached using Paperclip

Is there a way to make the users download the images attached using paperclip?
I just made a link like this:
link_to 'imagename', image.attachment.url
But this makes the browser open the image. I want the browser to ask the user to save the image. Is there a way to do that?
When it comes to sending a file you can find all information in here http://api.rubyonrails.org/classes/ActionController/Streaming.html There are most important cuts:
Simple download:
send_file '/path/to.zip'
Show a JPEG in the browser:
send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
Show a 404 page in the browser:
send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
to use one of this option you have to create a new action in controller like this:
class SomeController < ApplicationController
def download_file
send_file image.attachment.path
end
end
Here's a simple solution using the HTML5 download attribute
<%= link_to image.name, image.attachment.url, download: image.attachment.original_filename %>

rails how to render a file with correct filename

This is tough one to explain so i'll try my best, and hopefully edit the question if people need more information. I am not providing exact code, but merely an example of the issue.
I am using rails 2.3.8. I am on Unix.
I have a bunch of files under a directory not Apache accessible. (i.e. /data/files/file.rpk)
I have the following in my view.
link_to "RPK File", :controller => 'mycontroller', :action=> 'myaction', :file => '/data/files/file.rpk'
I have the following in my controller.
def myaction
if FileTest.exists?(params[:file])
render :file => params[:file]
end
end
When i select the link on the page i get a download prompt for my desired file, but the name of the file is "myaction" instead of the filename.
Thoughts on how i could get it named correctly?
Sounds like a job for send_file. The x_sendfile option prevents that your workers keep busy while transferring the actual file. You can read more about that in this blogpost.
send_file path_to_file_on_filesystem, :type => "application/zip", :x_sendfile => true
You want to use send_data with the :filename option. See the API documentation.
You want to be extremely careful with this, though. Never ever trust the client/user! They will send file=../../../../etc/group or something in order to read arbitrary files on your system, so be very sure to sanitize that value before passing it to any file-reading methods.

Prawn PDF with Rails mailer?

I have successfully created an email that sends on creation of a Kase, but now I need to attach a PDF that is created on the fly by Prawn and Prawno. Basically when you visit a kase such as application.com/kase/1 you just append the URL with .pdf i.e. application.com/kase/1.
I spent ages getting the PDF to work and look how I wanted, but I can't figure out how to add the PDF to an auto sending email - mainly because I cannot work out how to give it a link as it's auto generated.
Has anyone ever managed to get this to work?
Thanks,
Danny
I suppose it would be better if you store generated pdf somewhere - for caching purposes, etc.
But with current configuration, you can read generated page with Net::HTTP and attach response:
require 'net/http'
def your_mailer_method(record)
#...
attachment "application/pdf" do |a|
a.body = Net::HTTP.get('yourdomain.com', "/kase/#{record.id}.pdf")
a.filename="your_pdf_name.pdf"
end
end
You really should consider just not using Prawnto, and creating a subclass of Prawn::Document to do what you need. Then, in both your controller and your mailer code, it should just be:
MyReport.new.render
See the Prawn documentation on this:
http://wiki.github.com/sandal/prawn/using-prawn-in-rails
For the newer ones, you dont really need to send a request again, when you can ::
mail.attachments["invoice.pdf"] = {:mime_type => "application/pdf" , :content => pdf_generator}
Instead of doing this ::
send_data pdf.render , :filename => file_name_here , :type => "application/pdf"
just do this ::
pdf.render , :filename => file_name_here , :type => "application/pdf"
Do not send_data, just render that pdf in your email attachment as mentioned in the first snippet.
In fact, i just wrote a Gist on github.
This code works for me
def send_file(file, subject, text, to_email)
#subject = subject
#text = text
attachments["#{invoice.invoice_number}.pdf"] = file
from_email = abc#xyz.com
mail(:to => to_email, :from => from_email, :subject=> subject)
end

Resources