Test contents of file in rspec from respond_to format.csv - ruby-on-rails-3

I have the following code in my controller that exports a csv file
...
def export
#filename = 'users.csv'
#output_encoding = 'UTF-8'
#users = User.active_users #not the actual scope but this only returns active
respond_to do |format|
format.csv
end
end
...
And I have the following in my spec
it "should only return active users"
get :export, :format => :csv
# i want to check that my mocked users_controller#export is only returning the active users and not the inactive ones
end
response.body is empty in this test when i check it. How would I go about getting the csv file in the spec that is downloaded when this action is hit in a browser so that i can check the result? I've hit a bit of a wall trying to figure this out.
Thanks for any help you can provide.

A test that checks that a CSV file is being created is as follows, assuming that the controller action is at 'csv_create_path'
it 'should create a CSV file ' do
get csv_create_path
response.header['Content-Type'].should include 'text/csv'
end

You sort of specify that CSV format is supported, but not what the contents should be. You could do
respond_to do |format|
format.csv do
render text: File.read(#filename)
end
end
to actually render that CSV file.
If you also have a normal HTML formatted view for the same data, you would simply have
respond_to do |format|
format.html
format.csv do
render text: File.read(#filename)
end
end
assuming you have setup appropriate instance variables for the HTML view before.

Related

Rails goes to show action when I want custom action of open or expired

I am basically trying to do a custom index
In my routes.rb I have:
resources :tokens do
resources :visits
collection do
get :open
get :expired
end
end
My routes show as :
open_tokens GET /tokens/open(.:format) tokens#open
expired_tokens GET /tokens/expired(.:format) tokens#expired
I have open and expired defined in my tokens controller
def open
#q = Token.open_token.includes(:households, :visits).search(params[:q])
#tokens = #q.result
respond_to do |format|
format.html {render template: 'tokens/open'}
end
end
def expired
#q = Token.expired_token.includes(:households, :visits).search(params[:q])
#tokens = #q.result
respond_to do |format|
format.html {render template: 'tokens/expired'}
end
end
But it goes to my show controller with the following error:
Couldn't find Token with id=open
and it crashes at my Show action:
Better Errors shows the request as:
{"action"=>"show", "controller"=>"tokens", "id"=>"open"}
It seems to me that the action should be open with no parameters
What am I doing wrong?
I think I may have answered my question since it appears that Rails can only have one index per controller so I couldn't define another view that acts as an index.
So now what I want to do is pass a parameter so I can apply either an open scope or an expired scope

What does (.:format) mean in rake routes' output?

What does (.:format) mean in rake routes' output?
users GET /users(.:format) users#index
If you check the index action of your Users Controller then you will see something like this
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
So, this format is the type of response which will be generated.
In routes, a placeholder for the type of response is created irrespective of whatever format has been defined in the action of the controller.
So, if your URL is something like :-
users GET /users --> users/index.html.erb will be rendered
users GET /users.json --> users/index.json.erb will be rendered
Similarly, if you want response in PDF or xls format, then you just have to define format.pdf or format.xls and also you have to define these new MIME types which are not there by default in rails in some initializer file.
So, then if a request is made like :-
users GET /users.xls --> users/index.xls.erb will be rendered
Your routes file will then just look for the format.xls in the index action and respective view file means users/index.xls.erb will be rendered.

Rails 3 - set the filename in a respond_to

This seems like it should be simple, but I can't seem to find a straight answer.
I have added a csv mime-type, and the following seems to work, except that the downloaded file is always named "report.csv".
In my controller:
def report
respond_to do |format|
format.html
format.csv do
render :template => "summary/report.csv.erb",
:filename => "foo" #doesn't work
end
end
end
I think it's using the default renderer (I haven't implemented an alternate renderer), but I can't seem to find complete docs on the options available.
Isn't there something like a "filename" option or something that I can use? Is there a better approach?
I got it, thanks to some help from this answer.
format.csv do
response.headers['Content-Disposition'] = 'attachment; filename="' + filename + '.csv"'
render "summary/report.csv.erb"
end
First you set the filename in the response header, then you call render.
(The template param to render is optional, but in my case I needed it.)
You can pass the filename to send_data and let it handle the Content-Disposition header.
# config/initializers/csv_support.rb
ActionController::Renderers.add :csv do |csv, options|
options = options.reverse_merge type: Mime::CSV
content = csv.respond_to? :to_csv ? csv.to_csv : csv.to_s
send_data content, options
end
# app/controllers/reports_controller.rb
respond_to do |format|
format.html ...
format.csv { render csv: my_report, filename: 'my_report.csv' }
end
Then add a to_csv method to my_report or pass a pre-generated CSV string.
Alternatively you can use a combination of send_data and render_to_string (since you have a CSV template).
def report
respond_to do |format|
format.html
format.csv do
send_data render_to_string(:template => "summary/report.csv.erb"),
:filename => "foo"
end
end
end

How to render JSON in Rails view

I've tryed the solution of following example: In Rails, how do you render JSON using a view?
But my problem is that the database already has a JSON string saved, I pull the json string and then I should be able to display the JSON file in the the view.
I'm using this because an android tablet should be able to visit the same url but depending on its settings (send by a POST) a different JSON file should be displayed, the android tablet then fetches the json and use it to configure some options.
So I already have a full working json file, i'm looking for a way to display it in a view (without rendering html tags and other stuff). I tryed the following (yes I've added respond_to :json) :
# def show_json (#config is the record, #config.json_config is the actual json configuration file
#config = event.et_relationships.where(terminal_id: terminal).first
respond_to do |format|
format.json
end
Then my view I have
#show_json.json.erb
<%= #config.config_json %>
Then the HTML I get to see (no errors are given)
<html><head><style type="text/css"></style></head><body></body></html>
Thanks!
EDIT
I'm using rails 3.2.3
Here is my routes (only relevant parts)
match '/events/config', to: "events#show_json", as: :show_json
resources :events do
member do
get :select_item
get :category
end
end
Then also the controller (partial)
respond_to :html, :json, :js
def show_json
#terminal_id = params["mac"]
terminal_id = "3D:3D:3D:3D:3D:3D"
event = current_user.events.where("date(endtime) > ? AND date(starttime) < ?", Time.now.to_s, Time.now.to_s).first
if event.nil?
# Nothing got returned so we use the default event
event = current_user.events.where('"default" = ?', true).first
end
logger.info "MAC: #{terminal_id}"
terminal = current_user.terminals.where(serial: terminal_id).first
logger.info "Terminal: #{terminal.attributes.inspect}"
logger.info "#{event.attributes.inspect}"
#config = event.et_relationships.where(terminal_id: terminal).first
logger.info "CONFIG #{#config[:config_json]}"
respond_to do |format|
format.json { render json: #config[:config_json] }
end
end
Use render:
#config = event.et_relationships.where(terminal_id: terminal).first
respond_to do |format|
format.json { render json: #config }
end
And then you have path /:model/:action.json.

Rails: ZIP file shown in browser instead of being downloaded

My controller sends a ZIP file:
def index
respond_to do |format|
format.html { render :text => open("tmp/test1.zip", "rb").read }
end
end
PROBLEM: the ZIP is received as text shown in the browser.
I would like it to come as a download.
Note: I wrote format.html because when I write format.zip I get uninitialized constant Mime::ZIP. That is probably part of the problem.
You can register your own mime type:
Mime::Type.register "application/zip", :zip
def index
respond_to do |format|
format.html { ... } #do whatever you need for html
format.csv { ... } #do whatever you need for csv
format.zip { send_file 'your_file.zip' }
end
end
have a look here:
http://weblog.rubyonrails.org/2006/12/19/using-custom-mime-types
You should probably use ActionController::DataStreaming#send_file Take a look here:
http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file
You can skip the respond_to stuff and manually set the content type:
def index
render :file => '/full/path/to/tmp/test1.zip', :content_type => 'application/zip', :status => :ok
end
See the Layouts and Rendering in Rails guide for more information.
If you want to support .csv as well, then you could try looking at the params[:format]:
def index
if params[:format] == 'zip'
# send back the zip as above.
elsif params[:format] == 'csv'
# send back the CSV
else
# ...
end
end
And have a look at send_file as Marian Theisen suggests.
a simple solution for the user to download a file located in the application
def index
send_data File.read('/full/path/to/tmp/test1.zip'), filename: "test1.zip"
end
send_data will read your file located here /full/path/to/tmp/test1.zip and then send it as a response as a file
and your user download a file with filename test1.zip