Layouts and Rendering 1 (yield, content_for, content_for?)
I was puzzled when I looked at the following code (views/pages/home.html.haml) and the rendered page:
%h1= I18n.t('brand.name') %p Rails 4 PyGoogle app with social authentication and other goodies! %p = I18n.t 'brand.name' uses best practices and code extracted from several production projects. %p = link_to 'Github', 'https://github.com/PyGoogle/Rails4-PyGoogle.git'
It rendered to a page like this:
Well, the code is written in haml, and in erb, it looks like this:
<h1> <%= I18n.t('brand.name') %> </h1> <p> Rails 4 PyGoogle app with social authentication and other goodies! </p> <p> <%= I18n.t 'brand.name' %> uses best practices and code extracted from several production projects. </p> <p> <%= link_to 'Github', 'https://github.com/PyGoogle/Rails4-PyGoogle.git' %> </p>
Where is the css, header, and footer?
I realized there are still lots of things to learn. So, I looked up resources on rendering and page layouts.
The next sections (or probably subsequent chapters) are my logs of learning.
I followed this one: Layouts and Rendering in Rails.
Why do start off with "creating responses" when we want to learn about layouts and rendering?
Well, web server needs to respond back to a request, and the form of response is a page, rendered one, most likely, with some good looking layouts.
Controller is responsible for orchestrating the whole process of handling a request in Rails, and when it's time to send a response back to the user, the Controller hands things off to View.
This involves the processes of deciding what should be sent as the response and calling an appropriate method to create that response.
From the controller's point of view, there are three ways to create an HTTP response:
- Call render to create a full response to send back to the browser.
- Call redirect_to to send an HTTP redirect status code to the browser.
- Call head to create a response consisting solely of HTTP headers to send back to the browser.
When Rails renders a view as a response, it does so by combining the view with the current layout, using the rules for finding the current layout.
Within a layout, we have access to three tools for combining different bits of output to form the overall response:
- yield and content_for
- Asset tags
- Partials
In the following example for yield and content_for, we'll use the layout below (views/layouts/application.html.erb):
<title><%= content_for?(:title) ? yield(:title) : I18n.t('brand.name') %></title> <meta name="description" content="<%= content_for?(:description) ? yield(:description) : I18n.t('brand.name') %>">
The first line is for title and the second one is for description which is using the Ruby ternary operator.
Basically, it says, "if content_for?(:title) is present in the view file, use yield(:title) to include it, otherwise just display 'brand.name' which is 'PyGoogle'.
Within the context of a layout, yield identifies a section where content from the view should be inserted. The simplest way to use this is to have a single yield, into which the entire contents of the view currently being rendered is inserted:
<html> <head> </head> <body> <%= yield %> </body> </html>
We can also create a layout with multiple yielding regions:
<html> <head> <%= yield :head %> </head> <body> <%= yield %> </body> </html>
The main body of the view will always render into the unnamed yield. To render content into a named yield, we use the content_for method.
The following examples, in the title, we use the notation <%= ... %> to insert the title into the template using Ruby's yield function.
Note: the distinction between the two types of embedded Ruby is that <% ... %> executes the code inside, while <%= ... %> executes it and inserts the result into the template. The resulting page is exactly the same as before, only now the variable part of the title is generated dynamically by erb.
views/visitors/index.html.erb:
<title><%= yield(:title) %> | PyGoogle </title> <h3>Welcome!</h3> <p> This web application was created by <%= link_to('PyGoogle', 'http://pygoogle.com/') %>. </p>
This is rendered like the page below:
Another page, views/pages/about.html.erb, the yield enables us to replace just the page title:
<title><%= yield(:title) %> | PyGoogle </title> <h3>About the yield and content_for</h3> <p> This web application was created by <%= link_to('PyGoogle', 'http://pygoogle.com/') %>. </p>
Also note that since we've replaced the variable part of the page titles with erb, each of our pages has identical structure, including the contents of the title tag, with the sole exception of the material inside the body tag.
When we look into more deeper, we have a layout file in views/layouts/application.html.erb like this:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><%= content_for?(:title) ? yield(:title) : "PyGoogle Demo" %></title> <meta name="description" content="<%= content_for?(:description) ? yield(:description) : "PyGoogle Demo" %>"> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> </head> <body> <header> <%= render 'layouts/navigation' %> </header> <main role="main"> <%= render 'layouts/messages' %> <%= yield %> </main> </body> </html>
As we can see that the default Rails layout includes several additional lines:
<%= stylesheet_link_tag ... %> <%= javascript_include_tag "application", ... %> <%= csrf_meta_tags %>
This code arranges to include the application stylesheet and JavaScript, which are part of the asset pipeline, together with the Rails method csrf_meta_tags, which prevents cross-site request forgery (CSRF), a type of malicious web attack.
To see more clearly how it works, we creates dummy controller and view like this:
$ rails generate controller posts index ... create app/controllers/posts_controller.rb route get 'posts/index' create app/views/posts/index.html.erb ...
The views/posts/index.html.erb has dumb page:
Note that we do not have the appropriate title for the page such as "Posts Pygoogle". The new index page does not have any yield just dumb htmls:
<h1>Posts#index</h1> <p>Find me in app/views/posts/index.html.erb</p>
Actually, it is expected considering this file, views/layout/application.html.erb:
<title><%= content_for?(:title) ? yield(:title) : "PyGoogle Demo" %></title>
It tells rails just use "PyGoogle Demo" as a title if not yield nor content_for.
The content_for method allows us to insert content into a named yield block in our layout. For example, this view would work with the layout that we just saw:
<% content_for :head do %> <title>A simple page</title> <% end %> <p>Hello, Rails!</p>
the result of rendering this page into the supplied layout would be this HTML:
<html> <head> <title>A simple page</title> </head> <body> <p>Hello, Rails!</p> </body> </html>
The same example used for yield, we're now using content_for instead of yield:
<% content_for :title, "About|PyGoogle" %> <h3>About the yield and content_for</h3> <p> This web application was created by <%= link_to('PyGoogle', 'http://pygoogle.com/') %>. </p>
This will be rendered into this page:
Please note that the view file should work with the layout file.
To be fair, there are still other hidden elements such as collapse navbar and stylesheets are working behind the curtain.
Ruby on Rails
- Ruby On Rails Home
- Ruby - Input/Output, Objects, Load
- Ruby - Condition (if), Operators (comparison/logical) & case statement
- Ruby - loop, while, until, for, each, (..)
- Ruby - Functions
- Ruby - Exceptions (raise/rescue)
- Ruby - Strings (single quote vs double quote, multiline string - EOM, concatenation, substring, include, index, strip, justification, chop, chomp, split)
- Ruby - Class and Instance Variables
- Ruby - Class and Instance Variables II
- Ruby - Modules
- Ruby - Iterator : each
- Ruby - Symbols (:)
- Ruby - Hashes (aka associative arrays, maps, or dictionaries)
- Ruby - Arrays
- Ruby - Enumerables
- Ruby - Filess
- Ruby - code blocks and yield
- Rails - Embedded Ruby (ERb) and Rails html
- Rails - Partial template
- Rails - HTML Helpers (link_to, imag_tag, and form_for)
- Layouts and Rendering I - yield, content_for, content_for?
- Layouts and Rendering II - asset tag helpers, stylesheet_link_tag, javascript_include_tag
- Rails Project
- Rails - Hello World
- Rails - MVC and ActionController
- Rails - Parameters (hash, array, JSON, routing, and strong parameter)
- Filters and controller actions - before_action, skip_before_action
- The simplest app - Rails default page on a Shared Host
- Redmine Install on a Shared Host
- Git and BitBucket
- Deploying Rails 4 to Heroku
- Scaffold: A quickest way of building a blog with posts and comments
- Databases and migration
- Active Record
- Microblog 1
- Microblog 2
- Microblog 3 (Users resource)
- Microblog 4 (Microposts resource I)
- Microblog 5 (Microposts resource II)
- Simple_app I - rails html pages
- Simple_app II - TDD (Home/Help page)
- Simple_app III - TDD (About page)
- Simple_app IV - TDD (Dynamic Pages)
- Simple_app V - TDD (Dynamic Pages - Embedded Ruby)
- Simple_app VI - TDD (Dynamic Pages - Embedded Ruby, Layouts)
- App : Facebook and Twitter Authentication using Omniauth oauth2
- Authentication and sending confirmation email using Devise
- Adding custom fields to Devise User model and Customization
- Devise Customization 2. views/users
- Rails Heroku Deploy - Authentication and sending confirmation email using Devise
- Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger I
- Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger II
- OOPS! Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger (Trouble shooting)
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization