Caching in a multilanguage environment

2,302
-1

Published on

Published in: Technology, News & Politics
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,302
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
11
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Caching in a multilanguage environment

  1. 1. Caching in a multi- language environment Benjamin Krause <benjamin@omdb.org>
  2. 2. Caching in Rails
  3. 3. Caching in Rails ‣ Page Caching ‣ Action Caching ‣ Fragment Caching
  4. 4. Caching in Rails ‣ Page Caching ‣ ______________ Action Caching ‣ Fragment Caching
  5. 5. Caching in Rails Page Caching Fragment Caching routing routing routing before filters before filters before filters controller controller controller CACHED rendering rendering parts CACHED after filters after filters after filters illustration by Tammo Freese
  6. 6. Page Caching Webserver File.exists? !File.exists? __ _ ____ ___ ___ _ _ __ ____ __ ___ __ Mongrel __ _ ____
  7. 7. Fragment Caching Mongrel Rendering Fragment Cache
  8. 8. Caching in Rails ‣ No support for caching in different languages ‣ No support to expire all language- caches at once ‣ No support for language-negotiation
  9. 9. Content Negotiation let the user decide, what he wants to see
  10. 10. Content Negotiation consists of format negotiation and language negotiation
  11. 11. a simple resource http://www.omdb.org/movies/2062
  12. 12. Format Negotiation > HTTP “Accept” Header Accept: text/xml,application/xml, application/xhtml+xml,text/html > Rails supports format negotiation > Rails allows you to request a format explicitly (extra routes)
  13. 13. Format Negotiation http://www.omdb.org/movies/2062.html <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Ratatouille</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> http://www.omdb.org/movies/2062.xml <?xml version="1.0" encoding="UTF-8"?> <movie> <name>Ratatouille</name> <abstract> Remy is a rat, with a talent and passion for cooking. He dreams of one day working in a real restaurant
  14. 14. Format Negotiation class MoviesController < ApplicationController def show respond_to do |format| format.html { render } format.xml { render :xml => @movie.to_xml } format.js { render :json => @movie.to_json } end end end
  15. 15. Language Negotiation > HTTP “Accept-Language” Header Accept-Language: en-us,en;q=0.7,de;q=0.3 > Rails ignores the HTTP Header > Rails doesn’t add language routes
  16. 16. Language Negotiation
  17. 17. Language Negotiation http://www.omdb.org/movies/2062.html.en Routing Error no route found to match "/movies/2062.html.en" with {:method=>:get} http://www.omdb.org/movies/2062.xml.de Routing Error no route found to match "/movies/2062.xml.de" with {:method=>:get}
  18. 18. let’s fix that extending Rails
  19. 19. Language Routes extending Rails to set language routes
  20. 20. Language Routes # Adding default routes for # GET /movies/<id> # GET /movies/<id>.<format> # PUT /movies/<id> # PUT /movies/<id>.<format> # ... map.resources :movies map.resources :people map.resources :casts
  21. 21. Language Routes There are several possible routes, how to add a language parameter. http://www.omdb.org/movies/2062.html?lang=de http://de.omdb.org/movies/2062.html http://www.omdb.org/de/movies/2062.html http://www.omdb.org/movies/2062.de.html http://www.omdb.org/movies/2062.html.de
  22. 22. Language Routes There are several possible routes, how to add a language parameter. http://www.omdb.org/movies/2062.html?lang=de http://de.omdb.org/movies/2062.html http://www.omdb.org/de/movies/2062.html http://www.omdb.org/movies/2062.de.html http://www.omdb.org/movies/2062.html.de
  23. 23. Language Routes # Taken from Rails 1.2.3 # /actionpack-1.13.3/lib/action_controller/resources.rb # with slight modifications to fit this slide :-) action_options = action_options_for("show", resource) map.named_route( "#{resource.name_prefix}#{resource.singular}", resource.member_path, action_options ) map.named_route( "f_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", action_options )
  24. 24. Language Routes # Taken from Rails 1.2.3 # /actionpack-1.13.3/lib/action_controller/resources.rb # with slight modifications to fit this slide :-) action_options = action_options_for("show", resource) { :conditions => { :method => :get }, :requirements => { :id map.named_route( => /[^/;.,?]+/ }, :action => "show" } "#{resource.name_prefix}#{resource.singular}", resource.member_path, action_options ) map.named_route( "f_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", action_options )
  25. 25. Language Routes # Taken from Rails 1.2.3 # /actionpack-1.13.3/lib/action_controller/resources.rb # with slight modifications to fit this slide :-) action_options = action_options_for("show", resource) { :conditions => { :method => :get }, :requirements => { :id map.named_route( => /[^/;.,?]+/ }, :action => "show" } "#{resource.name_prefix}#{resource.singular}", resource.member_path, action_options ) map.named_route( "f_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", action_options ) /movies/:id
  26. 26. Language Routes # Extending # ActionController::Resources::map_new_actions # ActionController::Resources::map_member_actions # and others to support a language parameter map.named_route( "fl_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format.:lang", action_options )
  27. 27. Language Routes # Extending # ActionController::Resources::map_new_actions # ActionController::Resources::map_member_actions # and others to support a language parameter map.named_route( "fl_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format.:lang", action_options ) { :conditions => { :method => :get }, :requirements => { :id => /[^/;.,?]+/ :lang => /^fr|de|en$/ }, :action => "show" }
  28. 28. Language Routes http://www.omdb.org/movies/2062.html.en <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Ratatouille</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> http://www.omdb.org/movies/2062.xml.de <?xml version="1.0" encoding="UTF-8"?> <movie> <name>Ratatouille</name> <abstract> Remy is a rat, with a talent and passion for cooking. He dreams of one day working in a real restaurant
  29. 29. Accept-Language extending Rails to know about the requested language
  30. 30. Accept-Language # in environment.rb # # These are custom extensions, this is not part # of Rails AC = ActionController AC::AbstractRequest.default_language = :en AC::AbstractRequest.acceptable_languages = :fr, :de, :en
  31. 31. Accept-Language # Taken from Rails 1.2.3 # /actionpack-1.13.3/lib/action_controller/request.rb # Returns the accepted MIME type for the request def accepts @accepts ||= if @env['HTTP_ACCEPT'].to_s.strip.empty? [ content_type, Mime::ALL ] else Mime::Type.parse(@env['HTTP_ACCEPT']) end end
  32. 32. Accept-Language class ActionController::AbstractRequest def accepts_languages begin @accepts_languages ||= @env['HTTP_ACCEPT_LANGUAGE'].split(",").collect {|l| l.gsub(/;.*$/, '').gsub(/-.*$/,'').downcase.to_sym }.push(default_language).uniq @accepts_languages.delete_if { |l| !acceptable_languages.include?(l) } rescue NoMethodError @accepts_languages = default_language.is_a?(Array) ? default_language : [ default_language ] end end end
  33. 33. Accept-Language Accept-Language: fr => [ :fr, :en ] Accept-Language: fr; q=1.0, en; q=0.5, fi; q=0.2 => [ :fr, :en ] Accept-Language: en-us,en;q=0.7,de;q=0.3 => [ :en, :de ] Accept-Language: fi, es => [ :en ]
  34. 34. Accept-Language class ActionController::AbstractRequest def language params[:lang] || accepts_languages.first end end class ApplicationController before_filter :set_language def set_language @language = session[:lang] || request.language # for Globalize: # @language = Locale.set(@language) # for Gettext: # params[:lang] = @language unless params[:lang] end end
  35. 35. Language Routes http://www.omdb.org/movies/2062.html.en <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Ratatouille</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> http://www.omdb.org/movies/2062.xml.de <?xml version="1.0" encoding="UTF-8"?> <movie> <name>Ratatouille</name> <abstract> Eine Gourmet-Ratte muss so einiges erleiden, um ihre Lust auf exquisite Speisen befriedigen zu können.
  36. 36. Caching view caching, that is
  37. 37. Page Caching extending Rails to support language-based page caching
  38. 38. Page Caching Webserver File.exists? !File.exists? __ _ ____ ___ ___ _ _ __ ____ __ ___ __ Mongrel __ _ ____
  39. 39. Page Caching class MoviesController < ApplicationController caches_page :show def show respond_to |format| do format.html { render } format.xml { render :xml => @movie.to_xml } format.js { render :json => @movie.to_json } end end end
  40. 40. Page Caching GET /movies/2062 Mongrel caches_page ~/public/movies/2062.html
  41. 41. Page Caching # Taken from Rails 1.2.3 # /actionpack-1.13.3/lib/action_controller/caching.rb def cache_page(content = nil, options = {}) return unless perform_caching && caching_allowed self.class.cache_page(content || response.body, url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))) end
  42. 42. Page Caching # Reimplementing the cache_page method module ActionController::Caching::Pages def cache_page(content = nil, options = {}) return unless perform_caching && caching_allowed self.class.cache_page(content || response.body, url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format], :lang => request.language))) end end
  43. 43. Page Caching GET /movies/2062 Mongrel caches_page ~/public/movies/2062.html.en
  44. 44. Page Caching curl http://www.omdb.org/movies/2062 -H 'Accept: application/xml' => ~/public/movies/2062.xml.en
  45. 45. Page Caching curl http://www.omdb.org/movies/2062 -H 'Accept-Language: fr, en, de' => ~/public/movies/2062.html.fr
  46. 46. Page Caching curl http://www.omdb.org/movies/2062 -H 'Accept-Language: fr, en, de' -H 'Accept: application/xml' => ~/public/movies/2062.xml.fr
  47. 47. Apache how-to use Apaches content negotiations
  48. 48. Apache # Taken from # http://mongrel.rubyforge.org/docs/apache.html # Rewrite to check for Rails cached page RewriteRule ^([^.]+)$ $1.html [QSA] # Redirect all non-static requests to cluster RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]
  49. 49. Apache # Taken from # http://bugs.omdb.org/browser/trunk/conf/webserver/apache.conf # Perform content-negotiation and send to mongrel # if not cached. RewriteCond %{DOCUMENT_ROOT}/%{LA-U:REQUEST_URI} !-f RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]
  50. 50. Apache Apache 2.2 mod_rewrite documentation: %{LA-U:variable} can be used for look-aheads which perform an internal (URL-based) sub-request to determine the final value of variable.
  51. 51. Apache # Prefer text/html over other Accept Headers # Simplified RewriteConditions RewriteCond %{HTTP_ACCEPT} text/html RewriteCond %{SCRIPT_FILENAME} !.html$ RewriteCond %{IS_SUBREQ} 'false' RewriteRule ^/(.*)$ /%{SCRIPT_FILENAME}.html [QSA] # Perform content-negotiation and send to mongrel # if not cached. RewriteCond %{DOCUMENT_ROOT}/%{LA-U:REQUEST_URI} !-f RewriteCond %{IS_SUBREQ} 'false' RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]
  52. 52. GET /movies/2062 Apache Subrequest Apache /movies/2062.html.en Content Negotiation File.exists? !File.exists? __ ___ __ _ __ __ __ __ ___ __ __ _ _ __ ___ __ Mongrel ___ __ _ _ _ __ __ _ _ __
  53. 53. Apache # Let the user change the language in your # application SetEnvIf Cookie "language=(..)" prefer-language=$1
  54. 54. Fragment Caching extending Rails to support language-based fragment caching
  55. 55. Fragment Caching <% cache do %> <div class="person"> <div class="image"> <%= img_tag image_url(cast.person.image) %> </div> <strong> <%= link_to cast.person.name, person_url(cast.person) %> </strong> </div> <% end %>
  56. 56. Fragment Caching cached content MemCache
  57. 57. Fragment Caching <% cache :lang => request.language do %> <div class="person"> <div class="image"> <%= img_tag image_url(cast.person.image) %> </div> <strong> <%= link_to cast.person.name, person_url(cast.person) %> </strong> </div> <% end %>
  58. 58. Fragment Caching <% cache :lang => request.language do %> <div class="person"> <div class="image"> <%= img_tag image_url(cast.person.image) %> </div> <strong> <%= link_to cast.person.name, person_url(cast.person) %> </strong> </div> <% end %>
  59. 59. Fragment Caching ActionController::Base.fragment_cache_store = :mem_cache_store, "localhost"
  60. 60. Fragment Caching ActionController::Base.fragment_cache_store = OMDB::Cache::FragmentCache.instance
  61. 61. Fragment Caching ActionController::Base.fragment_cache_store = OMDB::Cache::FragmentCache.instance # Setting the default MemCache server OMDB::Cache::MemCacheStore.servers = 'localhost'
  62. 62. Fragment Caching cached content MemCache
  63. 63. Fragment Caching cached content MemCache MemCache MemCache :fr :de :en
  64. 64. Fragment Caching cached content Namespace Namespace Namespace :fr :de :en
  65. 65. Fragment Caching http://www.omdb.org/movies/2062.html.fr MemCache MemCache MemCache :fr :de :en
  66. 66. Fragment Caching http://www.omdb.org/movies/2062.html.de MemCache MemCache MemCache :fr :de :en
  67. 67. Fragment Caching http://www.omdb.org/movies/2062.html.en MemCache MemCache MemCache :fr :de :en
  68. 68. Fragment Caching # Delete the fragment for one language expire_fragment :controller => 'movies', :action => 'show', :id => 2062, :lang => :en # delete the fragment for all languages expire_fragment :controller => 'movies', :action => 'show', :id => 2062,
  69. 69. Tests how to test the views
  70. 70. Test Page Caching def test_caching_show accept :xml # language is a custom extension language :de page = "/casts/#{casts(:first).id}.xml.de" assert_cached_page( page ) do get :show, :id => casts(:first).id assert_response :success end end
  71. 71. Test Page Caching def test_expire_show accept :xml language :de page = "/casts/#{casts(:first).id}.xml.de" assert_no_cached_page( page ) do put :update, :id => casts(:first).id, :job => Job.composer.id assert_response :success end end
  72. 72. Test Fragment Caching def test_expire_fragment accept :js language :en params = { :controller => 'casts', :id => casts(:first).id, :template => 'cast' } assert_cached_fragment( params ) do put :update, :id => casts(:first).id, :job => Job.composer.id assert_template 'cast' end assert_no_cached_fragment( params ) do casts(:first).save end end
  73. 73. Where to get the plugins The plugins are available from http://svn.omdb-beta.org/plugins/mlr http://svn.omdb-beta.org/plugins/mlcache There will be an official announcement on http://blog.omdb-beta.org/
  74. 74. thank you

×