Zero Bytes Sent When Sending Axlsx Gem Generated File From Sinatra - ruby

Attempting to prompt a download window and stream an XLSX file using Ruby Sinatra and the AXLSX gem, my excel file serializes successfully to local file, so I know its a valid excel doc, but I need it to transfer content to the end user. There haven't been any docs online with examples of AXLS and Sinatra used together, only rails. Help is appreciated!
class Downloads < Sinatra::Base
get '/downloads/report' do
## ...
Axlsx::Package.new do |p|
p.workbook.add_worksheet(name: 'tab name') do |sheet|
## ...
end
content_type 'application/xlsx'
attachment 'cost-code-dashboard.xlsx'
p.to_stream # unsuccessful
# p.to_stream.read # unsuccessful as well
end
end
end
I have also tried the following snippet unsuccessfully
Axlsx::Package.new do |p|
## ...
send_file p.to_stream.read, type: "application/xlsx", filename: "cost-code-dashboard.xlsx"
end

It appears that the issue had everything to do with how Axlsx::Package.new was called, the helper functions were not available inside Axlsx, the following solution worked - online documentation said that the below content_type was better
get '/downloads' do
content_type :'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
p = Axlsx::Package.new
p.workbook.add_worksheet(name: 'Test') do |sheet|
sheet.add_row ['Hello world']
end
p.to_stream
end

Related

Ruby: Display SNMP output in Sinatra

i am trying to make a little website with Sinatra, where i want to display SNMP data.
require 'sinatra'
#require 'sinatra/reloader'
require 'snmp'
get'/' do
'Hello World'
SNMP::Manager.open(:host => 'localhost') do |manager|
response = manager.get(["sysDescr.0","sysName.0"])
response.each_varbind do |vb|
puts "#{vb.name.to_s} #{vb.value.to_s} #{vb.value.asn1_type}"
end
end
end
Unfortunately this code outputs the result on the console and not on the Web Page.
I hope you can help me.
It looks like your calling puts as you iterate through your data, this will print the results to the console as ruby cannot input items directly onto the web page, and because puts is only able to print into your console/ terminal. if you want to display the results on your web page you will need to pass them as params in to your :erb file, then display them within the erb file like so:
get'/' do
'Hello World'
SNMP::Manager.open(:host => 'localhost') do |manager|
#response = manager.get(["sysDescr.0","sysName.0"]) # add the # symbol to then pass as params into the erb file
end
erb(:index) # load up your erb file
end
then simply load your values in the erb file like so
<%=#response.each_varbind do |vb|%>
<p>
<%={vb.name.to_s} + {vb.value.to_s} + {vb.value.asn1_type}%>
</p>
<%end%>
Now the controller will load the index.html.erb file whenever the route get('/') is called and you should see your values displayed within the paragraph tag on screen
Hope that helps!

Create a plugin that puts text in a .txt file

I am working on creating a plugin in Ruby.
On this moment I am unable to insert the coordinates, that are added to a Sketchup model, in a .txt file.
This is my code:
require 'sketchup.rb'
SKETCHUP_CONSOLE.show rescue Sketchup.send_action("showRubyPanel:")
$stdout = File.new('file.txt', 'w')
module HWmakemyownplug
def self.fileplug
model = Sketchup.active_model
#Make some coordinates.
coordinates = [[2,0,39],[0,0,1],[1,1,0]]
#Add the points in Sketchup. This works!
coordinates.each { |point| model.active_entities.add_cpoint(point) }
#Puts the coordinates to the textfile 'file.txt'. This doesn't work!
$stdout.puts(coordinates)
end #def self.fileplug
end #module makemyownplug
if (!file_loaded?(__FILE__))
#Add to the SketchUp tools menu
extensions_menu = UI.menu("Plugins")
extensions_menu.add_item("testtesttest") { HWmakemyownplug::fileplug }
# Let Ruby know we have loaded this file
file_loaded(__FILE__)
end
The coordinates have to be printed when I click on menu > plugins > testtesttest.
You forgot to close file after $stdout.puts(coordinates)
$stdout.close
Here is an example code for writing data to a JSON file instead of a simple text document.
The code can run outside of SketchUp for testing in the terminal. Just make sure to follow these steps...
Copy the code below and paste it on a ruby file (example: file.rb)
Run the script in terminal ruby file.rb or run with SketchUp.
The script will write data to JSON file and also read the content of JSON file.
The path to the JSON file is relative to the ruby file created in step one. If the script can't find the path it will create the JSON file for you.
module DeveloperName
module PluginName
require 'json'
require 'fileutils'
class Main
def initialize
path = File.dirname(__FILE__)
#json = File.join(path, 'file.json')
#content = { 'hello' => 'hello world' }.to_json
json_create(#content)
json_read(#json)
end
def json_create(content)
File.open(#json, 'w') { |f| f.write(content) }
end
def json_read(json)
if File.exist?(json)
file = File.read(json)
data_hash = JSON.parse(file)
puts "Json content: #{data_hash}"
else
msg = 'JSON file not found'
UI.messagebox(msg, MB_OK)
end
end
# # #
end
DeveloperName::PluginName::Main.new
end
end

Can't upload image to Cloudinary from Sinatra

I'm trying to upload an image to Cloudinary from within my Sinatra web app. I
got it saving the image locally by using CarrierWave and I know Cloudinary and
CarrierWave play nicely with each other but I've tried everything and no dice.
I've followed the docs but they are very Rails-centric. Damn you Rails!
# Model - User.rb
class ImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process convert: 'png'
version :face do
cloudinary_transformation gravity: :face
end
end
class User
include DataMapper::Resource
attr_accessor :image
property.............
mount_uploader :image, ImageUploader
end
# Config - app.rb
require 'carrierwave/datamapper'
require 'cloudinary'
# DataMapper code ...
# Some routes ...
post '/add' do
user = user.create(name: params[:name])
Cloudinary::Uploader.upload(params[:image], api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME')
# View (haml)
%head
%script{src: "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"}
%script{src: "js/cloudinary_js/jquery.ui.widget.js"}
%script{src: "js/cloudinary_js/jquery.iframe-transport.js"}
%script{src: "js/cloudinary_js/jquery.fileupload.js"}
%script{src: "js/cloudinary_js/jquery.cloudinary.js"}
# snip snip ...
%form{action: "/add", method: "POST", enctype: "multipart/form-data"}
%input{type: "file", name: "image"}
%button Submit
The error I'm getting when trying to submit the form is:
TypeError at /add
no implicit conversion of Hash into String
The line it gives me that is causing the error is the Cloudinary uploader line in the post method.
Here's my params[:image] hash
{:filename=>"batman_robin.jpg", :type=>"image/jpeg", :name=>"image", :tempfile=>#<Tempfile:/var/folders/rv/2w8gpstn4hb2lb__27n27ksh0000gn/T/RackMultipart20131223-36484-tgmaba>, :head=>"Content-Disposition: form-data; name=\"image\"; filename=\"batman_robin.jpg\"\r\nContent-Type: image/jpeg\r\n"}
In the docs it mentions a file signature then states that CarrierWave handles this. I think my problem lies with the upload form.
Where am I going wrong with this? Anyone know what 'cl_image_upload_tag' generates and what are Cloudinary's "special attributes" that I need to add to my file upload tag?
UPDATE: So I figured that and I can actually just pass the image directly like so:
Cloudinary::Uploader.upload(params[:image][:filename] ...
However it now says
No such file or directory - batman_robin.jpg
which is strange. I've hardcoded a URL in there and it gets uploaded to Cloudinary. Any ideas? Also, any idea about the Cloudinary jQuery upload?
params[:image] is a hash but Cloudinary::Uploader.upload expects a string as the first argument. All the examples in the docs give a name or a URL, so pass the :filename from the params, e.g.
post '/add' do
user = user.create name: params[:name]
image_meta = params[:image]
filename = image_meta.delete :filename
Cloudinary::Uploader.upload filename, {api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME'}.merge(image_meta)
I'd also put all those default settings into a settings hash (and have them read in from the environment if they're secret), e.g.
config do
set :cloudinary_api, {api_key: ENV['API_KEY'],
api_secret: ENV['API_SECRET'],
cloud_name: ENV['CLOUD_NAME']}
end
then the call becomes…
Cloudinary::Uploader.upload filename, settings.cloudinary_api.merge(image_meta)
Instead of the filename you need to pass the actual content which is hold in Tempfile So passing Cloudinary::Uploader.upload(params[:image][:tempfile]) should work.

How to stream response rails app on heroku

I have a rails 3.1 app running on heroku.
I need to provide the user with the ability to download csv data.
I'm trying to stream the data, but it is all sent in one go.
Which for larger requests will timeout.
There is much talk on the heroku site about streaming and chunking
but as far as I can tell thin collects all the data and sends it in one go.
How do I get it to work?
Do I have to add some middleware? e.g. unicorn
The code streams fine running with mongrel.
I'm pretty sure you just need to add
stream
to the top of your controller.
More info on HTTP streaming can be found on RailsCasts: http://railscasts.com/episodes/266-http-streaming
This question is really old but the issue is still very common because of the 30'' limit in Heroku responses so I will add some code on how I achieved it. Works with Rails 5.2 & 6.1 on Heroku with Puma server.
I'm using #send_stream method (present only in edge rails, future rails 7) so I just copied it + set the Last-Modified header manually. Added all in a rails concern to reuse it.
module Streameable
extend ActiveSupport::Concern
include ActionController::Live
def send_stream(filename:, disposition: 'attachment', type: nil)
response.headers['Content-Type'] =
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
'application/octet-stream'
response.headers['Content-Disposition'] =
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename) # for Rails 5, use content_disposition gem
# extra: needed for streaming correctly
response.headers['Last-Modified'] = Time.now.httpdate
yield response.stream
ensure
response.stream.close
end
end
class ExporterController < ApplicationController
include Streameable
def index
respond_to do |format|
format.html # index.html
format.js # index.js
format.csv do
send_stream(attachment_opts) do |stream|
stream.write "email_address,updated_at\n"
50.times.each do |i|
line = "user_#{i}#acme.com,#{Time.zone.now}\n"
stream.write line
puts line
sleep 1 # force slow response for testing respose > 30''
end
end
end
end
end
private
def attachment_opts
{
filename: "data_#{Time.zone.now.to_i}.csv",
disposition: 'attachment',
type: 'text/csv'
}
end
end
Then, if you use something like curl you will see the output generated second by second.
$ curl -i http://localhost:3000/exporter.csv
An important thing is to write your code to iterate the data with #each, by using the Enumerable module. Oh, a tip with ActiveRecord, use #find_each so the DB fetch is in batches.

Download a zip file through Net::HTTP

I am trying to download the latest.zip from WordPress.org using Net::HTTP. This is what I have got so far:
Net::HTTP.start("wordpress.org/") { |http|
resp = http.get("latest.zip")
open("a.zip", "wb") { |file|
file.write(resp.body)
}
puts "WordPress downloaded"
}
But this only gives me a 4 kilobytes 404 error HTML-page (if I change file to a.txt). I am thinking this has something to do with the URL probably is redirected somehow but I have no clue what I am doing. I am a newbie to Ruby.
My first question is why use Net::HTTP, or code to download something that could be done more easily using curl or wget, which are designed to make it easy to download files?
But, since you want to download things using code, I'd recommend looking at Open-URI if you want to follow redirects. Its a standard library for Ruby, and very useful for fast HTTP/FTP access to pages and files:
require 'open-uri'
open('latest.zip', 'wb') do |fo|
fo.print open('http://wordpress.org/latest.zip').read
end
I just ran that, waited a few seconds for it to finish, ran unzip against the downloaded file "latest.zip", and it expanded into the directory containing their content.
Beyond Open-URI, there's HTTPClient and Typhoeus, among others, that make it easy to open an HTTP connection and send queriers/receive data. They're very powerful and worth getting to know.
NET::HTTP doesn't provide a nice way of following redirects, here is a piece of code that I've been using for a while now:
require 'net/http'
class RedirectFollower
class TooManyRedirects < StandardError; end
attr_accessor :url, :body, :redirect_limit, :response
def initialize(url, limit=5)
#url, #redirect_limit = url, limit
end
def resolve
raise TooManyRedirects if redirect_limit < 0
self.response = Net::HTTP.get_response(URI.parse(url))
if response.kind_of?(Net::HTTPRedirection)
self.url = redirect_url
self.redirect_limit -= 1
resolve
end
self.body = response.body
self
end
def redirect_url
if response['location'].nil?
response.body.match(/<a href=\"([^>]+)\">/i)[1]
else
response['location']
end
end
end
wordpress = RedirectFollower.new('http://wordpress.org/latest.zip').resolve
puts wordpress.url
File.open("latest.zip", "w") do |file|
file.write wordpress.body
end

Resources