What exactly is a template engine? - ruby-on-rails

I have done some googling and I understand that the template engine of Ruby is erb but what does a template engine mean?

Template engine are nothing but presentation layer, that represent actual logic/variable of rails application in html format. So end user can get what they are looking for.
Consider if you have #users object and you want to loop through each record and represent name of each user detail on webpage. There is no way in simple html to do so. So erb and other template engines comes in picture.
These engines covert/evaluate values of variables, functions and user defined syntax to html format and represent to end user.
Hope this helps you.

Template is a pattern, mold, or the like, usually consisting of a thin plate of wood or metal, serving as a gauge or guide in mechanical work that determines or serves as a pattern; a model:
- Dictionary Definition
In our world of Programming, The template is a framework of any repetitive work sharing a common pattern. We extract out the common pattern as a template, now you can embed the changing/dynamic data and create your copy of the work.
Example: You notice forms you get in hospitals or Govt. offices where there are blank-fields for you to write your dynamic data. At last, you get your copy of the application. Don't you?
To facilitate this we have ERB as an engine in the standard library of Ruby.
Let's get our hand dirty; Try this and see results:
require 'erb'
your_template = <<-TEMP
This document is a template for <%= title %> <%= full_name %>.
Now we are going to have a line written <%= n %> times.
<% n.times do %>
This line must repeat <%= n %> times.
<% end %>
TEMP
title = 'Mr.'
full_name = 'Shiva Gaire'
n = 5
generated_doc = ERB.new(your_template).result
puts generated_doc
Output
This document is a template for Mr. Shiva Gaire.
Now we are going to have a line written 5 times.
This line must repeat 5 times.
This line must repeat 5 times.
This line must repeat 5 times.
This line must repeat 5 times.
This line must repeat 5 times.

Related

Ruby on Rails: How to define a controller definition per shim?

First time asking something here on StackOverflow. So excited!
Basically, I'm wondering what the correct design pattern is for what I'm trying to accomplish. I have all my code working but was wondering it there's a more elegant, "RoR Way" to put it all together. For a language/framework so beautiful, it just feels like I've done this wrong:
I have a single master layout page ("WeekSummary") I'm using to display a bunch of "DaySummary" shims. All shims derive from the same template "_day_summary.html.erb."
On WeekSummary, I'm able to pass variables to individual shims fairly easily, eg:
<%= render 'layouts/day_summary', date: '2016-08-12' %>
<%= render 'layouts/day_summary', date: '2016-08-11' %>
But now I'm having trouble invoking a "day_summary" controller definition per each shim. Essentially, at this point in the render lifecycle, I believe I've already passed through the "C" part when the RoR engine called my "week_summary" definition (in which I did hold some business logic). But now I want the RoR engine to go back to the controller and call a "day_summary" controller definition per each shim I've defined on WeekSummary view page. I would like all variables/definitions to be then locally scoped to each shim (so I can reuse the same var names, etc).
I wasn't able to figure out how to do that though so right now I've simply dumped all my shim-specific business logic at the top of the "_day_summary.html.erb" in a massive <% %> block. Having so much business logic there in a View shim seems wrong though.
I hope this made sense. Does anyone have any suggestions for how to properly do this? Essentially, I'm trying to encapsulate the rendering of each shim into its own MVC lifecycle/pattern, if that makes sense. Thank you!
Edit: In response to kcdragon's code request on what's happening inside each shim:
So, for example, for each day_summary shim, I wish to calculate that day's pnl.
At the top level, in the week_summary controller def, I get all transactions:
#transactions = Transaction.all.order('ticker', 'date DESC')
Then in each shim, I filter #transactions by only the date I care about for that shim. Thus, a sample of each shim's business logic includes the below-- in this example, calculating that day's PnL:
transactions = #transactions.where(date: '2016-08-08')
pnlHash = Hash.new
totalPnl = 0
transactions.each do |t|
if !pnlHash.key?(t.ticker)
pnlHash[t.ticker] = t.pnl
else
pnlHash[t.ticker] += t.pnl
end
totalPnl += t.pnl
end
<%= totalPnl %> is then rendered elsewhere on the shim.
There's other business logic too that happens in the shim, but this is a good representative sample.
Now, obviously at the top level (week_summary), I could "pre-process" all daily PnL calculations and then store them in some massive hashtable which I'd then use to extract values per day_summary shim. (In this example, I guess that'd be a true model of the week_summary view.) But I don't want to do that. I want to encapsulate day_summary into its own thing, where all its business logic and rendering is processed on the fly as week_summary renders. Hopefully that makes sense?
Edit2: For sake of clarity, here's what each day_summary shim looks like. On the week_summary view, five of these guys are rendered, each one corresponding to its respective date:
Here's what the day_summary shim looks like.

can't convert String into Integer from inside hash

I am trying to display a given value from a block of search data per result in the listing. I am getting the error "can't convert String into Integer" How do I display the next level array inside my linked_agents?
My code for the particular column is such
<td>
<% if result['primary_type'] === "resource" or result['primary_type'] === "digital_object" or result['primary_type'] === "accession" %>
<%= display_agents(result) %>
<% end %>
</td>
and the code
def display_agents(hash, opts = {})
object = JSON.parse( hash["json"] )["linked_agents"]
object2 = object["_resolved"]
html = "<div class='audit-display-compact'><small>"
html << "<ul style='list-style-type:none'>"
html << "<li>#{object2}</li>"
html << "</ul>"
html << "</small></div><div class='clearfix'></div>"
html.html_safe
end
Here is what is inside linked_agents.
[{"_resolved"=>{"names"=>[{"sort_name"=>"John Smith"}]}}]
How can I get all the sort_name's to display the data? There could be more than one _resolved each containing a sort_name.
Thanks
More data would've been more helpful than less data, but essentially your problem is this: "json" contains a string. Nothing more. Not an object. Nothing you could reference with results[]. Assuming the JSON is all properly formatted, what you would need to do is something like this:
<%= JSON.parse( result["json"] )["linked_agents"]["_resolved"]["sort_name"] %>
Now, this is horribly sloppy, and not the way you're going to want to accomplish your goals (assuming this is even effective with your data set). In actuality, what you'll want to do is parse that JSON into a variable, and work through in hash form.
It is difficult to clearly keep the topics at work here because there isn't a clear question. I would encourage you to make sure you have a basic familiarity of ruby arrays, hashes, and nested structures consisting thereof. Additionally, take a look at how you're retrieving the data that goes into results["json"].
There are many ways to accomplish many things once your data is properly structured, but you need to have a clear understanding of these types of structure before you can work magic (and you need to possess a basic knowledge of what you aren't understanding before we can provide you with solutions).
I hope that is helpful. Feel free to discuss points of confusion in the comment, and I will update this answer as we approach a solution for your problem.

How to structure Util classes in RoR

I have a template that users can upload that generates a report. They can put special tags into the html template and it will replace with data from the db. Quick example:
<div class="customer-info">
<h1>{customer_name}</h1>
<h2>{customer_address_line1}</h2>
<h2>{customer_address_line2}</h2>
<h2>{customer_address_city}, {customer_address_state} {customer_address_zip}</h2>
</div>
I have a controller that looks up the customer and then parses the template and replaces the tokens.
Right now I have the parse code in the controller creating a fat controller. Not good.
But where should I move the code? Model folder? Create a Util folder and put it there?
Just not sure what the Rails Way would be.
I was curious about this too, and found a very similar discussion here. Honestly, I think it depends on how much parse code there is. If there are only a very few lines, then the model is a safe place. If it's going to be a large package, especially a re-usable one, the /lib/ folder may be a better bet for the parsing itself. However, you definitely should remove it from the controller, as you suggested.
I agree that the logic shouldn't be in the controller, but let's get a
little more specific about how you'd go about implementing this.
First, where are you storing your templates in the database? They
should be stored in their own model, let's call it
CustomerTemplate and give an attribute :template of type Text.
So now we have two types of objects, Customers and
CustomerTemplates. How to render a customer given a template? It
honestly wouldn't be terrible to just have a render function in
the CustomerTemplate model that takes a customer and renders it, but
it is putting some logic inside your app that doesn't strictly belong
there. You should separate out the "customer specific rendering logic"
from the "rendering my simple custom template language".
So, let's create a simple template handler for your custom language,
which I'm going to nickname Curly. This handler should know nothing about
customers. All it does is take a string and interpolate values inside
{}'s. This way if you want to add new template types in the future —
say, to render another model like an invoice — you can use the same
template type.
Templates in Rails are classes which respond to call and are
registered with ActionView::Template. The simplest example is Builder.
Here's a quickly written Template handler which renders Curly. The
call function returns a string which is eval'd, so the string has to
be valid ruby code. The string eval in scoped by the render call, so
it has access to any variables passed in via the { locals: {} }
option to render.
# In lib/curly_template_handler.rb
class CurlyTemplateHandler
def self.call(template)
src = template.source
"""
r = '#{src}'.gsub(/{([^}]*)}/) { |s|
local_assigns[$1.to_sym] || s
}
raw r
"""
end
end
Make sure the handler is initialized, and let's set it to listen for
the :curly type.
# In config/initializers/register_curly_template.rb
ActionView::Template.register_template_handler(:curly, CurlyTemplateHandler)
We need to add lib/ to autoload_paths so the class is loaded:
# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
Finally, we can render our template in our view! I'm embedding the string here, but you'd really get it from a CustomerTemplate object:
<%= render(inline: "<h2>{customer_name}</h2><p>{customer_address}</p>",
type: :curly,
locals: { customer_name: #customer.name,
customer_address: #customer.address }) %>
DO NOT USE MY EXAMPLE CODE IN PRODUCTION! I left out a bunch of corner
cases which you'll need to handle, like sanitizing user input.

Rails way to offer modified attributes

The case is simple: I have markdown in my database, and want it parsed on output(*).
#post.body is mapped to the posts.body column in the database. Simple, default Activerecord ORM. That column stores the markdown text a user inserts.
Now, I see four ways to offer the markdown rendered version to my views:
First, in app/models/post.rb:
# ...
def body
markdown = RDiscount.new(body)
markdown.to_html
end
Allowing me to simply call #post.body and get an already rendered version. I do see lots of potential problems with that, e.g. on edit the textfield being pre-filled with the rendered HMTL instead of the markdown code.
Second option would be a new attribute in the form of a method
In app/models/post.rb:
# ...
def body_mardownified
markdown = RDiscount.new(body)
markdown.to_html
end
Seems cleanest to me.
Or, third in a helper in app/helpers/application_helper.rb
def markdownify(string)
markdown = RDiscount.new(string)
markdown.to_html
end
Which is used in the view, instead of <%= body %>, <%= mardownify(body) %>.
The fourth way, would be to parse this in the PostsController.
def index
#posts = Post.find(:all)
#posts.each do |p|
p.body = RDiscount.new(string).to_html
#rendered_posts << p
end
end
I am not too familiar with Rails 3 proper method and attribute architecture. How should I go with this? Is there a fifth option? Should I be aware of gotchas, pitfalls or performance issues with one or another of these options?
(*) In future, potentially updated with a database caching layer, or even special columns for rendered versions. But that is beyond the point, merely pointing out, so to avoid discussion on filter-on-output versus filter-on-input :).
The first option you've described won't work as-is. It will cause an infinite loop because when you call RDiscount.new(body) it will use the body method you've just defined to pass into RDiscount (which in turn will call itself again, and again, and so on). If you want to do it this way, you'd need to use RDiscount.new(read_attribute('body')) instead.
Apart from this fact, I think the first option would be confusing for someone new looking at your app as it would not be instantly clear when they see in your view #post.body that this is in fact a modified version of the body.
Personally, I'd go for the second or third options. If you're going to provide it from the model, having a method which describes what it's doing to the body will make it very obvious to anyone else what is going on. If the html version of body will only ever be used in views or mailers (which would be logical), I'd argue that it makes more sense to have the logic in a helper as it seems like the more logical place to have a method that outputs html.
Do not put it in the controller as in your fourth idea, it's really not the right place for it.
Yet another way would be extending the String class with a to_markdown method. This has the benefit of working on any string anywhere in your application
class String
def to_markdown
RDiscount.new(self)
end
end
#post.body.to_markdown
normal bold italic
If you were using HAML, for example in app/views/posts/show.html.haml
:markdown
= #post.body
http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#markdown-filter
How about a reader for body that accepts a parse_with parameter?
def body(parse_with=nil)
b = read_attribute('body')
case parse_with
when :markdown then RDiscount.new(b)
when :escape then CGI.escape(b)
else b
end
end
This way, a regular call to body will function as it used to, and you can pass a parameter to specify what to render with:
#post.body
normal **bold** *italic*
#post.body(:markdown)
normal bold italic

Rails 3: Where should a helper that uses h (i.e. html_escape) live?

I'm writing a webapp in Ruby on Rails 3. Rails 3 automatically escapes any potentially-bad strings, which is generally a good thing, but means if you assemble HTML yourself, you have to call html_safe on it.
I have a Card model, which has several text fields, the contents of which are not trusted (may contain evil HTML or script). I have a function which performs a few transforms on one of these text fields, using other knowledge about the specific Card, to produce HTML output. I want to embed the HTML produced by this function in several places throughout several parts of my app.
Conceptually, this helper is to do with the View. However, I can't find any way to write functions in my View files; it seems they have to go in Helpers or the Controller/Model.
Since this function is very much specific to a Card object, the next best option would be to have a function inside my Card model card.rb:
class Card < ActiveRecord::Base
[...]
def format(unsafe_text)
initial_text = h unsafe_text # aka html_escape unsafe_text
# assembles HTML output based on initial_text and fields of self
output_text.html_safe!
end
Then I'd like to call this in assorted views by doing things like:
Rules text: <%= format(#card.rulestext) %>
However, there's a big problem here as well. In the Card model card.rb, I am able to use the html_safe! function, but I'm not able to use h or html_escape. It seems that the h and html_escape functions are only available in ERB views, not in the helpers or controllers!
There are a few workarounds. I can make format not sanitize its input, and go
Rules text: <%= format(h(#card.rulestext)) %>
But that's both prone to dangerous slipups (one missing h() and we've got problems) and is very non-DRY. At the moment I'm using a partial to gain access to the h() function:
(in a normal view)
Rules text: <%= render 'formattext', :text=> #card.rulestext %>
(app/views/shared/_formattext.html.erb)
<%= #card.format(html_escape(text)) %>
But this still feels dangerous. All I have to do is make single forgetful call to format(sometext) in a view, rather than calling render 'formattext', :text=> sometext, and I've got unescaped text running around.
Is there any better way to do this? Is there a way to write helper functions to live in the View rather than the Model or the Controller?
Place the logic that does your view assembly into a CardHelper:
app/helpers/card_helper.rb
class CardHelper
def rules(card)
initial_text = h card.rules_text
# assembles HTML output based on initial_text and fields of card
output_text.html_safe
end
end
It's not clear from your example whether you want to format several fields via the format method. If that's the case, then you might be able to do:
class CardHelper
def format(card, attribute)
initial_text = h card[attribute]
# assembles HTML output based on initial_text and fields of card
output_text.html_safe
end
end
You can use this helper like any other:
class CardsController
helper CardHelper
end
and in your views:
<%= rules(#card) %>
or
<%= format(#card, :rules) %>
Escaping the content for view is a View responsibility, this is the reason why the h helper is not available in controllers or models.
Still, I don't understand why can't you simply sanitize the content in the view.
Also note that, in Rails 3, you don't need to call the h helper.
Content is sanitized automatically by default unless you flag it as html_safe!.
The main reason why is not logically true to use the h helper in the model is because the model should work view-independently. In other words, the model should not care whether the content is going to be embedded in a HTML document or JSON file (which requires a different escaping approach compared to HTML).

Resources