Generate HTML using grape API in rails - ruby-on-rails

I have a requirement where I need to generate/spit out HTML markup from one of my APIs. I am using grape API but cannot find a way to throw out HTML markup. I can specify content-type as text/html and create a HTML markup but is there a better way to achieve this like rendering a template similar to below:
render template:'my_template' locals: {:data => data}
and 'my_template' (HTML) can take care of how the page looks like ? render is an undefined method in GrapeAPI so not sure what other stuff I can use ?

I think it's quite a bad idea to use an API only framework to render HTML...
Nevertheless, you should be able to use the :txt content-type to simply render out your string like you described.
You could use ERB for that, as it is part of the standard-library and pretty easy to use:
require "erb"
class Template
attr_reader :name, :data
def initialize(name, data)
#name = name
#data = data
end
def build
raw = File.read("templates/#{name}.erb")
ERB.new(raw).result(binding)
end
end
as far as I read, grape automatically uses the to_s method of an entity to render :txt, so you could implement something like this in your model:
def to_s
Template.new(self.class.to_s.downcase, self)
end
it might also be possible to register a html content type and write some kind of formatter that does this kind of stuff.

Related

PDF from ERB template with PDFKit and Rails

In my Rails application I'd like to have a special route to download a custom PDF.
This PDF should be generated via PDFKit from an ERB template in my application. Instead of describing what I'd like to achieve, I better paste some non-executable but commented code:
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render :download_my_list_as_pdf
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
# This is the message I'd like to show at the end
# But using 'render' more than once is not allowed
render plain: 'Download complete'
end
end
I wasn't able to find an answer to this problem yet, any help would be greatly appreciated!
render_to_string(*args, &block)
Raw rendering of a template to a string.
It is similar to render, except that it does not set the response_body
and it should be guaranteed to always return a string.
render does not return a string it sets the response_body of the response.
class MyController < ApplicationController
def download_my_list_as_pdf
# The template uses the instance variables below
#user_id = params[:user_id]
#items = ['first_item', 'second_item']
# This line describes what I'd like to do but behaves not like I want ;)
# Render the ERB template and save the rendered html in a variable
# I'd also use another layout
rendered_html = render_string(:download_my_list_as_pdf)
kit = PDFKit.new(rendered_html, page_size: 'A4')
kit.to_pdf
pdf_file_path = "#{Rails.root}/public/my_list.pdf"
kit.to_file(pdf_file_path)
send_file pdf_file_path, type: 'application/pdf'
end
end
However if you are sending a file you cannot send text or html as well. This is not a limitation of Rails but rather how HTTP works. One request - one response.
Usually javascript is used to create notifications surrounding file downloads. But consider first if its really needed as its pretty annoying to users as the browser usually tells you that you downloaded a file anyways.

Is there a way to cache a string fragment within a helper method?

I have a helper that generates complex HTML for common components in an engine.
Helper (very simplified):
def component(name)
component = Component.find_by_name!(name)
# a whole lot of complex stuff that uses component to build some HTML
end
View:
<%= component(:my_component) %>
I want to implement fragment caching on these components but I want to do it within #component itself to keep things DRY, e.g.
def component(name)
...
cache some_unique_fragment_name do
html
end
# or, more succinctly:
cache(some_unique_fragment_name, html)
end
The problem is that Rails' cache helper expects it's going to wrap a block of HTML in Erb and therefore won't work as I've described above.
Is there a way to use #cache for a string fragment in a helper method?
I'm a big fan of the fetch block, you can read more in the Rails docs:
def component(name)
# ...
cache.fetch('some_unique_fragment_name') do
html
end
end
What this does is it will return the value of some_unique_fragment_name if it's available, otherwise it will generate it inside the block. It's a readable, clean way of showing that caching is occurring.

Render ERB Template in RABL Template

I have a scenario where I'd like to pass back a long message with my JSON. Instead of writing it out with string concatenation I'd rather put together an erb template that I can render into my JSON. Below is the code I'm currently trying:
object #invitation
node(:phone_message) do |invitation|
begin
old_formats = formats
self.formats = [:text] # hack so partials resolve with html not json format
view_renderer.render( self, {:template => "invitation_mailer/rsvp_sms", :object => #invitation})
ensure
self.formats = old_formats
end
end
Everything works as expected the first time this code is run, however, I run into problems the second time I run it because it says there is a missing instance variable (which I assume was generated and cached during the first run).
undefined method
_app_views_invitation_mailer_rsvp_sms_text_erb___2510743827238765954_2192068340
for # (ActionView::Template::Error)
Is there a better way to render erb templates into rabl?
You could try using ERB as standalone, and not going through the view renderer, like so:
object #invitation
node(:phone_message) do |invitation|
begin
template = ERB.new(File.read("path/to/template.erb"))
template.result(binding)
end
end
binding is a method on Object (through the Kernel module) and it returns the binding which holds the current context, which also includes instance variables (#invitation in this case)
Update:
Don't really know if this will help you get any further (and I also realised it's been more than a year since you posted this), but here's another way to render ERB templates in a standalone fashion:
view = ActionView::Base.new(ActionController::Base.view_paths, {})
class << view
include ApplicationHelper
include Rails.application.routes.url_helpers
end
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
view.render(:file => "path/to/template.html.erb", :locals => {:local_var => 'content'})
When I have time I should actually try this with Rabl.

What is the best way to return multiple variables and arrays formatted in xml, json etc with respond_with

I'm looking for a good way to return xml or json from a controller including multiple variables. For example:
def index
#ad = Ad.some_annoying_ad
#map = Map.some_map_for_something
#articles = Articles.trending
respond_with #articles
end
How would I best add the #ad and #map var to the #articles array? I have seen people using the merge function, but I am not sure if that's what I'm looking for. Just want to know which way is most standard, flexible, DRY. Thanks!
Note: I am aware that respond with will automatically format the results in xml or json depending on the file extension added to the url. Thanks!
Instead of merging #ad, #map, just create create a new hash then put add the arrays to it, like
respond_with({:ad => #ad, :map => #map, :article => #article})
It just render all the datas with groups.
I would recommend you to take a look at RABL (stands for Ruby API Builder Language) gem (cast, github). It offers you a DSL for defining the structure of your JSON/XML response in templates (like Haml or CoffeeScript does).
In your case it can be like this:
# in *.json.rabl or *.xml.rabl
object false
child(#ad) { attributes :field1, :field2 }
child(#map) { attributes :field3, :field4 }
child(#article) { attributes :body => :content } # remap is easy!
There is even partials support for DRYing your code. Worth trying.

Rails: In ApplicationController is their a to render xml by default?

I want to be able to render both html and xml. By default html is rendered unless we add a format e.g. /myresource.xml. I want to render xml by default and only respond to .html format:
GET /myresource/ returns html
GET /myresource.xml returns xml
I would like:
GET /myresource/ returns xml
GET /myresource.html returns html
Is there an easy way to achieve this?
Regards,
Chris
Since this nonstandard in Rails, it might best be solved via mod_rewrite in Apache. Map /whatever to /whatever.xml and map /whatever.html to /whatever.
If you want to control this at the controller level you can do the following (either in the ApplicationController or the MyResourceController)
class MyResourceController (or ApplicationController) < ApplicationController
before_filter :change_format
...
protected
def change_format
params[:format] = 'xml' if params[:format].blank?
end
end
try adding :format => 'xml' to your resource definition, though i'm not sure restfull stuff should work this way

Resources