For example, Ruby on Rails's page caching mechanism, rubyrails

Source: Internet
Author: User
Tags processing text

For example, Ruby on Rails's page caching mechanism, rubyrails

With the page cache, Rails can no longer intervene. To some extent, this is a good thing, because you can indeed achieve excellent performance. Rails only needs to create an HTML page and put it into a directory. After that, you can leave it behind. Since then, the application server has managed these pages, and there is no need to cycle the page into the application server. From a performance perspective, page caching is a blessing.

I also love page caching and Rails makes it easy to use. Cache can be enabled with only one line of code. If you add some code, you can simply delete the file or use the Rails high-level API to terminate the cache. There is a problem here. Not every website can use the page cache. If the data on the page changes according to the user accessing the page, the page cannot be cached. In addition, if it is difficult to determine when the page expires and ends, it will find that the page cache requirements are too harsh.

For example, on almost every page, ChangingThePresent.org (see the sidebar) has some user data that varies according to the currently logged-on user. Figure 1 shows part of our latest homepage. (We have been trying to improve it, so it may change .) The problem displayed on this page is relatively simple. If you can determine whether the user has logged on, you can use Flash, JavaScript, DHTML or any other browser-based code to dynamically customize the view. You will find that logged-on users can log out of the system or view their configuration files, but logged-out users can register or log on again.
Figure 1. logon and logout views on ChangingThePresent.org

Figure 2 shows a slightly more advanced user data view, which is used by our site. The two views in Figure 2 are significantly different. To process the page cache, I must resolve all the differences first. For each logged-on user, I must replace the logout content on the page to display the login ID and image of the logged-on user. Caching this content poses another challenge, because each user's data is different.
Figure 2. Two completely different views

This situation is not exclusive to ChangingThePresent.org. If you need personalized user experience, the use of unmodifiable Rails page cache will be limited. However, if there are not many custom pages, these pages can be easily cached.

There are many solutions to these problems. I prefer the following skills:

  • Within the constraints of the Rails framework, cancel the page cache and use the segment cache to replace it.
  • First load most of the page, and then use JavaScript and Ajax to load the smaller dynamic part of the page. Server-side code can detect whether a user logs on and then use Ajax to display the appropriate part.
  • Store certain user statuses (such as whether the user has logged on) in the cookie of the client. Then, use JavaScript to dynamically change the appearance of the page based on the cookie content.

Among the three skills, I prefer the third one, because both the first and second skills involve the Rails application. To achieve maximum scalability, we need to use static content as much as possible. In this article, I will focus on introducing the third method. Do not use this method to store any sensitive data that cannot be lost, such as the ICBM startup code or credit card number. This method works well for the limited data we process.
Use Show and tell or hide and seek?

When I first tried to cache this home page, I could simply replace these links with JavaScript. We can regard this technique as Show-and-tell. Based on our understanding of logged-on users, we can use JavaScript to selectively replace or inject some of the content on the Web page, so as to provide users with the correct experience. For further subdivision, I will perform the following operations:

  • Create a Web page with only public elements of all users.
  • When a user logs on, some data about the user is stored in the cookie, such as the login information.
  • Then, use JavaScript to inject HTML segments based on the cookie content to fill the remaining parts of the page.

For the ChangingThePresent homepage, the show-and-tell skills are too powerful, because I only have two sets of links to Display Based on the logged-on users. So I chose the second technique, I call it hide-and-seek. First, the public page elements of all users are displayed, and the changes of the page are displayed through the hidden versions of each data. This is the hide part. Then, use JavaScript to find and display the user's content in the file based on the user's role. This is the seek part. You may think that it is too powerful to display all possible data versions. In fact, this method is very common when you choose to enable multiple features for different security roles. The hide-and-seek method is ideal for ChangingThePresent homepage. To implement this method, you can perform the following operations:

  • Create a Web page with only public elements of all users.
  • Partition users by type. Add content versions for each user type. In my case, the ChangingThePresent Homepage contains the login user and logout user. First, make this content visible.
  • When a user logs on, some data that can be divided into user groups is stored in cookies, such as the user role or login status.
  • When a user accesses this page, the content version of the user type is displayed selectively.

Implement hide and seek

For the ChangingThePresent homepage, the implementation of hide-and-seek is very simple. In Figure 1, a section of the home page displays links related to user accounts. These links can be changed based on whether the user logs on. The primary task is to build all public content on this page. I have not provided specific practices in this article. The second page displays all the dynamic content of all users, regardless of whether the user has logged on:
Listing 1. Creating all versions of dynamic content in a single view

<div id='logged_out'> <%= link_to "login", :controller => 'members', :action => 'login' %> <br /> <%= link_to "register", :controller => 'members', :action => 'signup' %></div><div id='logged_in' style="display: none;"> <%= link_to "your profile", :controller => 'profiles', :action => 'show' %> <%= link_to "logout" , :controller => "members", :action => "logout" %></div>

You may have noticed the my profile link. At first, the link points to the user-specific configuration file, but this may hinder our homepage cache. Instead, I simply point this link to an index operation without any user ID. Then, the index operation redirects the user to the correct configuration file page:
Listing 2. Redirect the user to the correct configuration file page

  def index    redirect_to my_profile_url  end

In Listing 2, my_profile_url is a method that determines the correct Configuration File URL based on the user type (which may be a celebrity, Consultant, or member. Each user type has a separate configuration file page. At this time, the function of the program has been completed, you can see a total of four links, logged_in and logged_out each have two links:

  • Login
  • Register
  • Your profile
  • Logout

Next, obtain the cookie containing the current user type. For ChangingThePresent, I create a cookie when logging on, which contains the current Login ID. Then, destroy the cookie when logging out:
Listing 3. creating and destroying cookies during login and logout

def login if request.post?  self.current_user = User.authenticate(params['user_login'], params['user_password'])  ...  if logged_in?   set_cookies   ...  endenddef logoutendprivatedef set_cookies cookies[:login] = current_user.login cookies[:image] = find_thumb(current_user.member_image)enddef logout cookies.delete :login cookies.delete :image ...end

In listing 3, logged_in? Is a private method. If the current user is logged on, true is returned. The preceding Rails method creates three cookies upon logon and deletes them upon logout. There is no need to worry about data here. Data is still not required. It can be understood that, without calling the Rails framework, I can determine whether a user is logged on. I do not need to ensure that the cookie expiration termination is consistent with the site expiration termination rules. In my example, the two are consistent, so I can start to cache the page as much as possible.

Next, you can selectively hide and display the correct entries based on your cookies. Add the following JavaScript code to public/javascripts/application. js:
Listing 4. JavaScript code for logging on to the div using show and hide

function readCookie(name) {  var nameEQ = name + "=";  var ca = document.cookie.split(';');  for(var i=0;i < ca.length;i++) {    var c = ca[i];    while (c.charAt(0)==' ') c = c.substring(1,c.length);    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);  }  return null;}function handle_cached_user() { var login_cookie = readCookie('login');  var logged_in = document.getElementById('logged_in');  var logged_out = document.getElementById('logged_out');  if(login_cookie == null) {   logged_in.style.display = 'none';   logged_out.style.display = 'block';  } else {   logged_out.style.display = 'none';   logged_in.style.display = 'block';  }}

The first function reads the cookie value from Javascript, and the second function processes the DOM. You can simplify this code by using the Prototype library, but I have included basic DOM searches for your understanding. The last step is to call the JavaScript function during page loading. I added the following code to the layout:
Listing 5. calling JavaScript functions when a page is loaded

  <script type="text/javascript">   window.onload = function() {     handle_cached_user();     <%= render_nifty_corners_javascript %>      <%= yield :javascript_window_onload %>   }  </script>

The above JavaScript code is very simple. When a page is loaded, the handle_cached_user function will be loaded, and it will display or hide the correct content accordingly. Now, I can add the following code to the Controller to enable the page cache:

  caches_page :index

The above code works very well. I still need to regularly Delete the previous page from the cache so that I can terminate the expiration of this page. Therefore, I only need to delete public/index.html on a regular basis. The hide-and-seek method is very effective for pages with several types of users, but not for user partial shown in 2. For the latter, you need to use both hide-and-seek and show-and-tell techniques.
Implement show-and-tell

Let's take a look at Figure 2. I will use hide-and-seek-based on whether the user has logged on-select the correct version of partial, then, use the show-and-tell technique to fill in the dynamic section of the page based on the cookies I wrote in row 4 and row 5 in listing 3. Remember, for show-and-tell, I specifically changed the page elements to suit individual users.

First, complete the static content displayed on the two partial (that is, the logout user and login user. Assuming that the user has logged out, I will hide logged_in div by attaching the display: none style. Then, if necessary, I can use JavaScript to show or hide them. Note that I use the same two names: logged_in and logged_out to identify each div, so that I do not need to modify the JavaScript I wrote for this homepage:
Listing 6. Present the two partial logon and logout

<div class="boxRight sideColumnColor">  <div id='logged_in'>    <%= render :partial => 'common/logged_in' style="display: none; %>  </div>  <div id='logged_out'>    <%= render :partial => 'common/logged_out' %>  </div></div>

Next, complete the logged_in partial content. Note that each HTML component that contains dynamic content has an ID so that I can locate it using JavaScript and replace it:
Listing 7. Display logged_in partial

<div id='logged_in' style="display: none;"> <%= link_to %(<span class="mainBodyDark">Hi, </span>) +    %(<span class="textLarge mainBodyDark"><b id='bold_link'>) + "my_login" +    %(</b></span>), {:controller => 'profiles', :action => 'show', :id => 'my_login'}, {:id => 'profile_link'} %> <br/> <div id='picture_and_link'>   <a href="http://member/my_login" id='link_for_member_thumbnail'>     </a> </div> <div id="not_mine">Not my_login?</div> <br/> <%= image_button "logout", :controller => "members", :action => "logout" %>

If you have a good understanding of Rails, you may notice several of the customized helper functions. We can see the obvious dynamic content everywhere. I need to use JavaScript to replace the content for each loaded page: Three logins, one member image. The JavaScript code here makes a modification to the handle_cached_user function, and also contains a method for dynamic users to process page updates. Based on the specific situation in this article, I have simplified this code a little. You can add the following functions to the application. js file:
Listing 8. Replacing the user's partial Element

function handle_user_partial() { var login_cookie = readCookie('login'); var image_cookie = readCookie('image');  var profileLink = document.getElementById('profile_link');  profileLink.href = '/member/' + login_cookie;  document.getElementById('bold_link').firstChild.nodeValue=login_cookie;  document.getElementById('not_mine').firstChild.nodeValue="Not " + login_cookie + "?";  document.getElementById('link_for_member_thumbnail').href="/member/" + login_cookie;  document.getElementById('member_thumbnail').src=image_cookie.replace(/%2[Ff]/g,"/");  document.getElementById('member_thumbnail').alt=login_cookie;}

In listing 8, this JavaScript function first reads this cookie and obtains a part of the DOM tree: the link to the current user configuration file, called profile_link. Then there is the handle_user_partial function:

  • Replace the Login User Name (stored in login_cookie) with my_login to create a correct URL for the user configuration file page.
  • Insert the login user name to the DOM element, which uses bold text to indicate the login user.
  • Put the simple sentence "Not login ?" Insert to the DOM element, which contains the logout title in login partial.
  • Find the dom element that contains the member image, replace the general image URL with the member image URL, and save the member image in image_cookie.
  • In addition, replace the alt mark of the image with the login name to prevent the image from appearing.

During DOM navigation, you may find that you sometimes need to directly go to the DOM element, and sometimes you need to go to the specific sub-elements of the element, such as when processing text. I used the firstChild function to find the first child element of the DOM element as needed. Because the syntax is more friendly, the Prototype library makes it easier to process specific DOM elements, but this is beyond the scope of this article.

I have created all cookies. The last step is to call JavaScript from the handle_cached_user function. Remember that this function is in public/javascripts/application. js:
Listing 9. Add the handle_user_partial function to handle_cached_user

function handle_cached_user() { var login_cookie = readCookie('login');  var logged_in = document.getElementById('logged_in');  var logged_out = document.getElementById('logged_out');  if(login_cookie == null) {   logged_in.style.display = 'none';   logged_out.style.display = 'block';  } else {  handle_user_partial();   logged_out.style.display = 'none';   logged_in.style.display = 'block';  }}

Note that there are two additional lines of code under the handle_cached_user function in the else condition. The two lines of code can be replaced properly before the logged_in DOM element is visible. The rest is to cache the entire page using the page cache commands described in this article and the article last month.
Conclusion

This advanced technique introduced in this article opens the door for us. At ChangingThePresent.org, we estimate that a very simple time-based cleaning tool can cache more than 75% of pages. By using a slightly complex cleanup technique, we can cache more than 90% of pages, and more. If you want to influence our image cache plan, you can only access Web requests from the application server 1% to 3%.

But at the same time, we should also see the negative side. I added significant complexity to this system. I must maintain more complex HTML code and ensure that HTML and JavaScript are synchronized. But the good thing is that I can use the simplest and most effective caching technology to achieve better performance. You can also try this technique-access ChangingThePresent.org and load the home page. Next, load the menu at each top. You will find that we cache four of the six top menus on the page. Create an account and reload each menu. Can you guess which page is cached? In the next article, while continuing to explore Rails in the real world, I will show you some tips to improve ActiveRecord performance.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.