Rails create file and render - ruby-on-rails

In rails, is there a way (in a controller) to:
create a file
render a view or template to that file
redirect_to or render another view
I've tried all kinds of constructions, but keep getting the same error: Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action.
Otherwise; is it possible to render a template or view to a file without displaying that template/view?
thnx!
code:
def get_report
# part 1: create and render file for use with phantomjs
File.new('./vendor/assets/javascripts/graph_disk1.json','w') {|f| f.write(render "reports/disk", :layout => false)}
system `phantomjs ./vendor/assets/javascripts/highcharts-convert.js -infile ./vendor/assets/javascripts/graph_disk1.json -outfile ./app/assets/images/chart01.png -options ./vendor/assets/javascripts/resources.json`
# part 2: create odf-report and use image created bij phantomjs/highcharts-convert
report = ODFReport::Report.new("#{Rails.root}/app/report_templates/PSC2_KalScanV0.odt") do |r|
r.add_image :graphd1, "#{Rails.root}/app/assets/images/chart01.png"
send_data report.generate, type: 'application/vnd.oasis.opendocument.text',
disposition: 'attachment',
filename: 'report.odt'
end
end
the 2 parts work each, but not when called liked this (in 1 action/controller).

The solution is always easy once you've found it:
Instead of: f.write(render "reports/disk", :layout => false),
Use: f.write(render_to_string "reports/disk", :layout => false)
and voila, no more error

it seems you tried to create custom routes with render different file other than rails way, let me give you sample case, for example you have client controller but then you want to create custom method and routes other than 7 standard rails way
rails generate controller clients
inside routes.rb
resources :clients do
collection {
get :check_data # this to get data
post :import_data # this to post data
}
}
# prease remove all other routes for client controller that usually generated with get
inside app/controllers/client_controller.rb create two method for route above
def check_data
...
# the default view file is /views/clients/check_data.html.erb
# but you may also type like this below to render other file
# please note the first thing you must mention controllers name then the file name
render "clients/noname.html.erb"
end
def import_data
...
#
# here after client saved, it goes to other path instead of default
if #client.save
redirect_to courses_path
end
end

Related

Add RESTful Action

The source of my information is section 2.9 here:
[http://guides.rubyonrails.org/routing.html#connecting-urls-to-code][1]
What I'm trying to do is add a custom action "search" and corresponding view.
So, as it says to do in the documentation, I've added this code in my config/routes.rb file:
resources :dimensions do
collection do
get "search"
end
end
I've also defined in the dimensions_controller file:
def search
#dimensions = Dimension.all
respond_to do |format|
format.html # search.html.erb
format.json { render json: #dimensions }
end
end
I then stopped and restarted the rails server, but when I navigate to /dimensions/home, I'm still getting this error message:
Couldn't find Dimension with id=search
Also showing that my parameter is:
{"id"=>"search"}
So am I just missing another bit of code that gives the instruction to interpret /dimension/search as a collection action as opposed to the show action?
I've already confirmed that search_dimensions_path exists, so I know that the resource block in the routes.rb file is actually adding paths. It's just interpreting them as a separate search action that's giving me trouble.
Thanks for your time.
This code should work fine. Can you show us your routes.rb file?
On a side note, you probably don't want to have a separate action for searching, using the index action is the preferred way.
Found the issue:
I had to make the resource declaration in my config/routes.db file for dimensions after creating the collection action, like so:
resources :dimensions do
collection do
get "search"
end
end
resources :dimensions
Then everything worked as expected.

How does one use instance methods in a static method? I'm trying to async'ly create a document

In my controller, i have a method defined as:
def self.store_pdf(id)
...
end
in that method, I need to call render_to_string to render the correct file / layout:
render_to_string(
:action => "../view/current_version/show.pdf.erb",
:layout => false)
but because render_to_string is both an instance method and protected, I need to do the following:
me = self.new # self is the cortroller
me.send(:render_to_string,
:action => "../view/current_version/show.pdf.erb",
:layout => false)
but then there are dependencies such as the response object that render_to_string needs to work, as shown here: http://apidock.com/rails/ActionController/Base/render_to_string
So, I began adding them
me.send(:response=, ActionController::Response.new)
But, more and more of the global instance variables need to be defined, and I decided it was too much work just to try to get one static method to work.
The method needs to be static, so that delayed_job can run the method in the background at a later time.
Anyone have an idea as to how to pull this off?
You can read erb via ERB if you are not using any rails helper,If you are using any rails helper then include Rails helper.
you can refer using here or
require 'erb'
class PdfRender
#include ActionView::Helpers::OutputSafetyHelper
#include helper if any is present any
def self.render_pdf(id)
#set any instance variable if you are using in pdf
content = File.read('path/of/erb/template')
template = ERB.new(content)
# template content will give you text now you can render or generate pdf
template_content = template.result(binding)
end
end
Note:
replace h() with CGI.escapeHTML()

Creating custom view similar to index.html.erb

I'm creating a custom view that is a slight modification of the index.html.erb. I'd like to be able to create a link on my web app that directs a user to this custom view called list.html.erb.
Here's what I've done:
1) Copied the default scaffold index view and renamed it to list.html.erb
2) Modified GalleriesController by copying the index method and renaming to list:
def list
#galleries = Gallery.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #galleries }
end
end
3) Modified routes.rb file like so:
match "galleries/list" => "galleries#list"
I keep getting the following error:
Couldn't find Gallery with ID=list
Rails.root: /Users/scervera/Sites/MDN
Application Trace | Framework Trace | Full Trace
app/controllers/galleries_controller.rb:28:in `show'
In my search on stackoverflow I was unable to find any similar questions.
I'm guessing you put the match outside of, and after, the gallery resources routing.
This means the list is being interpreted as the :id of the default RESTful mapping.
Options include:
Just using index unless you truly need them both (which seems weird).
Adding a list RESTful action as described here (see below).
Changing the order of your routing and/or using a constraint to avoid route overlap. IMO this is the most-fragile and least-preferable.
To add the list action (option 2):
resources :galleries do
  get 'list', :on => :collection
end
You should put your galleries/list route before all other gallery routes.
Order matters. In your case, route "galleries/:id" gets matched first and causes this error.
You can get exhaustive information about Rails routing here: Rails Routing from the Outside In.

How do I create a temp file and write to it then allow users to download it?

I'm working on my first application and I need some help with allowing my users to download a text file with certain variables that are being displayed on the page.
Take a shopping list for example.
Let's say you allow your users to create a shopping list of products, and then display the shopping list with the items on a shopping list page,
e.g. localhost:3000/list/my-list
Take a look at the example code below (which is probably incorrect):
File.open('shopping_list.txt', 'w') do |file|
file.puts 'Item 1: #{product_1.name}'
file.puts 'Item 2: #{product_2.name}'
file.puts 'Item 3: #{product_3.name}'
end
Which then creates a text file that has the following content:
Item 1: Eggs
Item 2: Butter
Item 3: Bread
Users should then be able to download this file (i don't want this file to be stored on the server) via a download link.
I have no idea how to achieve this, but I'm hoping you guys can guide me. :D
TL;DR
create text files populated with model data (perhaps create a method to achieve this?)
text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)
I am assuming there is a resource for List with the attribute name as the name of the list and a list has_many Item which has an attribute description
First off, create a download path change your routes config/routes.rb
resources :lists do
member {get "download"}
end
Now if you run a rake routes in the console you should see a route like
/lists/:id/download
Whats more you should now have the helpers download_list_url & download_list_path to use in your view like
<ul>
<% #lists.each do |list| %>
<li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>
In your lists_controller add the action, and as you dont actually want to keep the file on the server disk just stream the data as a string
def download
list = List.find(params[:id])
send_data list.as_file,
:filename => "#{list.name}.txt",
:type => "text/plain"
end
Finally you see I have used a as_file method which you should add to the model (I prefer not to do this stuff in controllers, fat models, skinny controllers). So in the List model
def as_file
output = [self.name]
self.items.each {|item| output << item.description }
output.join("\n")
end
You say you don't want to store the file on the server, but "download" it on request; this sounds like you just want to generate and deliver a text document in response to the download link. There are several approaches, but you want to be sure of setting the mime-type so the browser sees it as a text file instead of an html document.
product_info = [
"Item 1: #{product_1.name}",
"Item 2: #{product_2.name}",
"Item 3: #{product_3.name}",
].join("\n")
render :text => product_info # implies :content_type => Mime::Type["text/plain"]
BTW, your example with open/puts above won't output what you think since single-quoted strings don't interpolate.
so, you wish to :
create text files populated with model data (perhaps create a method
to achieve this?)
text files should not be stored on the server, but
created as users click the download button (not sure if this is the
rails way but perhaps someone could show me a better way)
You have the right idea, here's what to do :
Create a method in your model to generate the text file contents. Let's say this method is called list_data
It seems like you have an existing controller action called my_list. Hence we can call our new method in the controller like so :
.
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end
To link to the download, just use link_to :action => my_list, :format => 'text'
See http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data for full docs on send_data
Caveat & explanations : Using the method above, there isn't really an explicit creation of files, Rails is streaming it for you. Hence this method is not suitable for very large files, or when the generation of the file content will take a while. Use a delayed method to generate the file and store it - the file contents somewhere if that's the case - but we can use send_data once it has been generated
You could try a combination of TempFile and send_file. In your controller action ..
file = Tempfile.new('foo')
file.write("hello world")
file.close
send_file file.path
At Rails 2.3 you can use Template Streaming. Working with Redmine I can remember something like that, you have to adapt for your case. Reference: Streaming and file downloads
require "prawn"
class ClientsController < ApplicationController
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, :align => :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Using the Thong Kuah you must just change the "content_type" param:
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end

Rails 3 rendering template missing without file ending

I have an action in my controller as below:
def show
#post = Post.find_by_setitle(params[:setitle])
if !#post
render 'error_pages/404'
return
end
respond_to do |format|
format.html
end
end
If the render error_pages/404 I get a template missing. Switching it to render error_pages/404.haml.html works fine.
Why is this?
N.B. There is no actual error_pages controller or model. Just a convenient place to keep them.
Edit: I'm using mongoid and hence don't have access to ActiveRecord. Controller base can't be looking for a particular ActiveRecord exception?
From the documentation
The render method can also use a view that’s entirely outside of your application (perhaps you’re sharing views between two Rails applications):
Rails determines that this is a file render because of the leading slash character. To be explicit, you can use the :file option (which was required on Rails 2.2 and earlier):
You need either to pass the :file option, or to start the location string with a slash. Alternatively, you could use the Rails functionality to rescue from errors, and recover from ActiveRecord::RecordNotFound with a 404. See this post for details.
You should probably use render :template => 'error_pages/404'.
I think Rails is looking for a partial called _404.
Try it out 1:
render 'error_pages/404' (and name the file _404.html.erb)
Try it out 2:
render :template => 'error_pages/404' (and name the file 404.html.erb i.e. no leading underscore)

Resources