How to add a theme to WordPress blog

how to add new theme to wordpress blog and how to add a theme to wordpress blog
Dr.MohitBansal Profile Pic
Dr.MohitBansal,Canada,Teacher
Published Date:26-10-2017
Your Website URL(Optional)
Comment
C H A P T E R 1 2 ■ ■ ■ Adding User-Created Themes to the Blogging Engine In Chapter 6, we added blogs to RailsCoders that allows users of the site to create their own blogs. Currently, all blogs on RailsCoders look similar to other pages on the site; there is no way for a user to personalize the look of a blog. This would be a desirable feature, because most bloggers wish to personalize their blogs, either by using or adapting an existing template or creating their own design from scratch. It is possible to allow users to create their own ERb templates, which could be stored in the database or just as files that are rendered when someone views the blogs. However, since we know that ERb allows any Ruby code to be embedded within it, including code that accesses the server’s file system, this would be a very bad idea. Malicious users could easily hack the site and cause a lot of problems. To solve this problem, we will use a templating plug-in for Rails called Liquid, which allows us to have user-editable templates that only have access to objects that you have specified and disallows embedded Ruby code within the templates. The Blog Template Requirements To add user-definable templates to the existing blogging system, we will need to modify the existing entries controller and create a new controller to allow users to edit their templates. We will call this controllerusertemplates_controller. We also need to add a model that will store the templates created by users. We will call this model Usertemplate. ■Caution We are using a model called “Usertemplate” rather than “Template,” because “Template” is a reserved word in Rails. If you try to create a model or controller called “Template,” you will simply end up with a lot of slightly confusing errors. We require a database table calledusertemplates to store the actual templates created by each user. 329 330 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E Each user can have a number of templates, so eachusertemplate object belongs to a user, requiring a userid field. Aname field is needed to define what type of template it actually is. This is so we can allow different templates for different blog pages, such as the standard blog entry list and the entry show page, showing one particular entry along with all comments for that entry. A templatebody field is required to hold the actual template itself. We will limit the body field to 10,000 characters. The database fields necessary for thisusertemplates table are shown in Table 12-1. Table 12-1. The usertemplates Database Schema Field Name Field Type Description id integer The primary key name string The name of the template body text The actual body of the template For the user’s blog pages, the templates that we will allow the user to define areblog_index, the main blog page listing the latest blog posts of a particular user, andblog_entry, a page for viewing a single blog entry that shows the blog entry along with any comments that have been left about this entry. When a user creates a new account on RailsCoders, they will not have defined any templates, so when rendering the blog views, we will check if the user has defined any templates. If no templates exist for the user or if the body field is empty, we will render the default RailsCoders entries templates. The usertemplates controller will allow our users to edit the body of their templates. It will follow the same structure as our other controllers, but we will not allow the user to create a new template—only to edit theblog_index andblog_entry templates. Also, they will not be allowed to delete a template. When the user visits theindex method of the usertemplates controller, we will check to make sure that the templatesblog_index andblog_entry exist in the database for the particular user. If not, we will create the objects with empty body fields, allowing them to edit them. Therefore, we must implement the methodsindex,edit, andupdate for the usertemplates controller. Liquid Templates As I mentioned, we will be using a Rails plug-in called Liquid to render the templates. Liquid was developed by Tobias Lütke, and you can find the Liquid Templates home page athttp:// home.leetsoft.com/liquid. Liquid allows users to create their own templates but prevents them from running inse- cure code on your server. It also allows you to store templates in a database. Since all of our users’ data is stored within a database, this makes it very easy for us to extend our database and store their personalized templates in a database too. C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 331 Liquid is in active development, so it is a good idea to keep an eye on and contribute to the Google Group athttp://groups.google.com/group/liquid-templates and the wiki accessible from the Liquid home page. The Liquid API Using Liquid is very simple. The simplest method of rendering a template is to instantiate a Liquid Template object and call theparse method with your template as a parameter. You can then render this object as HTML by calling therender method and passing in the necessary variables. For example, you could parse a very simple template like this: template = Liquid::Template.parse("Hello user, it is now time.") You could then render this using the given variables as follows: template.render( 'name' = 'Freddy', 'time' = Time.now.to_s ) The template we parsed only has access to the variables passed to it by therender method. However, if we pass a user object to therender method rather than just a string, the template would have access to all of the methods of the user object, including potentially dangerous methods. This then creates a similar problem when allowing the user to write ERb templates. To solve this, Liquid allows us to create drops. Liquid drops act as a wrapper around a regular Ruby object, allowing you to provide a user with just the attributes of an object that you define. They also allow you to add extra functionality to templates by creating extra accessible methods in a drop. We will create a number of drops, allowing our users to access safe versions of the User, Entry, and Comment models. Liquid also allows us to create filters. Liquid filters are simply Ruby methods that allow you to provide text filters to the template author. They take one parameter, perform some action, and return a string to be entered into the rendered template. Liquid provides a number of built-in filters, such asupcase,downcase,strip_html, and a date reformatter. After you have installed the Liquid plug-in, look at its source atvendor/plugins/liquid/lib/liquid/standardfilters.rb to see the provided filters. It is very simple to add new filters to meet the specific requirements of your application. Liquid Markup Liquid templates use two types of markup, similar to the ERb templates we have created for the rest of the application. In Liquid, output is surrounded by and, while tags, which control the logic in the template, are surrounded by% and%. For example, to output a user’s name, we might use markup like the following: Hello user.username As we mentioned previously, we can add filters to outputs, modifying the output to match our requirements. We can also chain filters together using (the pipe character). Some examples of filters to modify the given output follow: 332 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E Hello user.username upcase The time is now now date "%Y %h" Visit 'my blog ' link_to 'http://alanbradburne.com', 'Alan's Blog' Here’s what they do: � The first example takes the username of the user object and passes it through a filter calledupcase, which simply makes the username uppercase. � The next example formats the current time into a format specified by thedate filter. � The third example creates a link using thelink_to filter. This takes a URL and analt attribute, adding the relevant link to the string that is passed into the filter. We need to have some way of adding logic to the template to control what is output to the browser. We do this using Liquid’s tags. There are a number of built-in tags, includingcomment, if/else,for, andifchanged. These act in similar ways to the statements you are used to in Ruby. For example, to iterate through elements of an array using thefor loop, you would do the following: % for entry in entries % entry.title br / % endfor % Within afor loop, there are a number of helper variables available, such asforloop.first, forloop.last, andforloop.length. These can be very useful when designing templates, because they allow you to add special styling to items in tables or lists, depending on their position. Conditional statements are performed like this: % if user % user.username % else % No user selected % endif % For a full list of markup with examples, go to the Liquid project wiki athttp:// home.leetsoft.com/liquid/wiki/DesignerHowTo. As you can see, Liquid gives the user the tools to create complex templates while preventing direct access to the internals of the Rails application. Installing Liquid Liquid is installed as a Rails plug-in. Simply open a terminal window, and from the applica- tion’s directory, install with the following command: ruby script/plugin install svn://home.leetsoft.com/liquid/trunk/liquid/ A /Users/alan/Rails/railscoders/vendor/plugins/liquid A /Users/alan/Rails/railscoders/vendor/plugins/liquid/test A /Users/alan/Rails/railscoders/vendor/plugins/liquid/test/include_tag_test.rb A /Users/alan/Rails/railscoders/vendor/plugins/liquid/test/test_helper.rb C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 333 A /Users/alan/Rails/railscoders/vendor/plugins/liquid/test/helper.rb ... A /Users/alan/Rails/railscoders/vendor/plugins/liquid/example/server/templates/ products.liquid A /Users/alan /Rails/railscoders/vendor/plugins/liquid/example/server/templates/ index.liquid A /Users/alan/Rails/railscoders/vendor/plugins/liquid/README Exported revision 144. We can now access the Liquid methods from any controller. Building the Blog Templates Feature Now that we know what the feature must do and have Liquid installed, we can begin building the templating feature. Creating the Liquid Drops We are going to place the Liquid drop files that we write in the directoryapp/drops/. Create this directory now. In order for Rails to know to look in this directory, we need to add it to the Rails configura- tion. We will also add the directory that we will use to store the Liquid filters that we will write in the next section. Open theconfig/environment.rb file, and find theconfig.load_paths statement at line 23. The line will be commented out with an example directory as a parameter. Uncomment the line, and replace the example path with thedrops directory and thefilters directory (we will create these directories in a moment), as follows: ... Rails::Initializer.run do config ... Add additional load paths for your own custom dirs config.load_paths += %W( RAILS_ROOT/app/drops ➥ RAILS_ROOT/app/filters ) ... We now need to create the drops themselves. First of all, create the user drop file,app/ drops/user_drop.rb. Enter the code shown in Listing 12-1. Listing 12-1. The User Drop File class UserDrop Liquid::Drop def initialize(user) user = user end 334 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E def username user:username end def email user:email end def profile user:profile end def blog_title user:blog_title end end This class lists the only data that is available to a Liquid template when it is passed a user object. Other attributes, such ashashed_password, are not accessible, only theusername,email, profile, andblog_title. If you wish to give your users access to other attributes of the User model, you would just add them as extra methods here. Theinitialize method is executed when theUserDrop object is instantiated and the other methods simply return the relevant data. In order for this Liquid drop to be accessible to the template, we also have to add a method calledto_liquid to the User model. This method simply instantiates theUserDrop object and passes it to the Liquidrender method. Open the User model file,app/models/user.rb, and add theto_liquid method at the end of the file as follows: require 'digest/sha2' require 'rss/2.0' class User ActiveRecord::Base ... def to_liquid UserDrop.new(self) end end We now need to create the drops for the Entry and Comment models. Create the entry drop file,app/drops/entry_drop.rb, and add the code in Listing 12-2. Listing 12-2. The Entry Drop File class EntryDrop Liquid::Drop def initialize(entry) entry = entry end C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 335 def title entry:title end def body entry:body end def comments_count entry:comments_count end def permalink "/users/entry.user.id/entries/entry.id" end def comment_post_url "/users/entry.user.id/entries/entry.id/comments" end end Note that we have added a drop calledpermalink. This allows the user writing the template to easily access the URL of the entry by simply referring to an entry attribute calledpermalink. The drop calledcomment_post_path returns the path that a new comment should be posted to. This will be used on the entry view page to allow users to create new comments. Now add theto_liquid method to the Entry model. Open the fileapp/models/entry.rb, and add the method as follows: class Entry ActiveRecord::Base ... def to_liquid EntryDrop.new(self) end end Finally, create the comment drop file,app/drops/comment_drop.rb, and add the code in Listing 12-3. Listing 12-3. The Comment Drop File class CommentDrop Liquid::Drop def initialize(comment) comment = comment end def author comment.user.username end 336 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E def body comment:body end def created_at comment:created_at end end In this drop file, theauthor method provides the user with the attributeauthor. This is not a direct attribute of the Comment model, but we can access it and return it to the template as if it were. Now, open theapp/models/comment.rb file, and add the followingto_liquid method: class Comment ActiveRecord::Base belongs_to :entry, :counter_cache = true belongs_to :user validates_length_of :body, :maximum = 1000 def to_liquid CommentDrop.new(self) end end Creating the Liquid Filters Since our users can write blog entries using the Textile markup system, we need to add a Liquid filter allowing the templates to have access to atextilize method. To do this, we need to create a Liquid filter. We will also create a filter calledlink_to_entry, which will allow the user to easily link to an individual entry by just specifying the entry object and the filter. Create the directory for the Liquid filters that we specified in theconfig/environment.rb file,app/filters/. Next, create a filter file calledapp/filters/text_filters.rb, and enter the code in Listing 12-4. Listing 12-4. The Liquid Text Filters File module TextFilters include ActionView::Helpers::TagHelper def textilize(input) RedCloth.new(input).to_html end def link_to_entry(entry) content_tag :a, entry'title', :href = entry'permalink' end end C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 337 Thetextilize filter simply converts the input text, which is marked up with Textile, into HTML using RedCloth, the Ruby package for processing Textile markup. Thelink_to_entry filter takes anentry object as input and builds ana tag with a link to the entry’s permalink and the entry title as the link text. In order to use the Rails helper method content_tag, we have to include the ActionView code at the beginning of the file. We can now create theusertemplate model, controller, and views. The Usertemplate Model Generate the skeleton code for the Usertemplate model using the Railsgenerate script: ruby script/generate model Usertemplate exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/usertemplate.rb create test/unit/usertemplate_test.rb create test/fixtures/usertemplates.yml exists db/migrate create db/migrate/024_create_usertemplates.rb Edit the generated migration file, and add the fields as defined in the specification. Also, add an index on theuser_id andname fields, since that is how we will be retrieving the templates from the database. Open the migration filedb/migrate/024_create_usertemplates.rb, and edit it as shown in Listing 12-5. Listing 12-5. The Usertemplates Migration File class CreateUsertemplates ActiveRecord::Migration def self.up create_table :usertemplates do t t.column :user_id, :integer t.column :name, :string t.column :body, :text end add_index :usertemplates, :user_id, :name end def self.down drop_table :usertemplates end end Now, execute this migration using the following command: rake db:migrate 338 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E == CreateUsertemplates: migrating ============================================= create_table(:usertemplates) - 0.0281s add_index(:usertemplates, :user_id, :name) - 0.0322s == CreateUsertemplates: migrated (0.0607s) ==================================== We next need to edit the Usertemplate model file and add the relationship with the User model and a validation. Open the fileapp/models/usertemplates.rb, and edit it as shown in Listing 12-6. Listing 12-6. The Usertemplate Model File class Usertemplate ActiveRecord::Base belongs_to :user validates_length_of :body, :maximum = 10000 end We also need to add the reciprocal relationship to the User model. Open theapp/models/ user.rb file, and add thehas_many relationship as follows: require 'digest/sha2' require 'rss/2.0' class User ActiveRecord::Base ... has_many :friendships has_many :friends, :through = :friendships, :class_name = 'User' has_many :usertemplates def before_save ... The Usertemplates Controller We will now create the controller that allows a user to edit the user templates for an account. We will use thegenerate script to create the usertemplates controller: ruby script/generate controller Usertemplates exists app/controllers/ exists app/helpers/ create app/views/usertemplates exists test/functional/ create app/controllers/usertemplates_controller.rb create test/functional/usertemplates_controller_test.rb create app/helpers/usertemplates_helper.rb C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 339 Since this will act as a REST resource, we need to add the resource definition line to the routes file. Openconfig/routes.rb, and add themap.resources line as follows: ActionController::Routing::Routes.draw do map ... map.resources :photos map.resources :tags map.resources :usertemplates map.resources :users, :member = :enable = :put do users ... We should add a link to the controller in the sidebar menu too. Open the fileapp/views/ layouts/_menu.rhtml, and add the link to the usertemplates controller in the section only shown to logged-in users, as follows: ... % if is_logged_in? % liLogged in as: i%= logged_in_user.username -%/i/li li%= link_to 'My Profile', edit_user_path(logged_in_user) -%/li li%= link_to 'My Friends', friends_path(:user_id = logged_in_user) -%/li li%= link_to 'My Photos', user_photos_path(:user_id = logged_in_user) -%/li li %= link_to 'Upload Photo', user_new_photo_path(:user_id = logged_in_user) -% /li li %= link_to 'New Blog Post', new_entry_path(:user_id = logged_in_user) -% /li li%= link_to 'Blog Templates', usertemplates_path -%/li li %= link_to 'Logout', :controller = 'account', :action = 'logout', :method = :post % /li % else % Now, open the generated controller file,app/controllers/usertemplates_controller.rb, and enter the code in Listing 12-7. Listing 12-7. The Usertemplates Controller File class UsertemplatesController ApplicationController before_filter :login_required def index usertemplates = logged_in_user.usertemplates.find(:all) if usertemplates.empty? logged_in_user.usertemplates Usertemplate.new(:name = 'blog_index', :body = '') 340 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E logged_in_user.usertemplates Usertemplate.new(:name = 'blog_entry', :body = '') usertemplates = logged_in_user.usertemplates.find(:all) end end def edit usertemplate = logged_in_user.usertemplates.find(params:id) rescue ActiveRecord::RecordNotFound redirect_to :action = 'index' end def update usertemplate = logged_in_user.usertemplates.find(params:id) if usertemplate.update_attributes(params:usertemplate) flash:notice = 'Template was successfully updated.' redirect_to usertemplates_path end rescue ActiveRecord::RecordNotFound redirect_to :action = 'index' end end The code in this file does a number of things: � before_filter ensures that the methods in this file are accessible only by logged-in users and that the objectlogged_in_user is available. � When theindex action is executed, the program checks to see if the two user templates blog_index andblog_entry exist for the logged-in user. If they do not, they are created. � Theedit action simply retrieves theusertemplate object with the specifiedid, as long as it belongs to the logged-in user. � update saves the updated user template. Next, we need to create the view for this controller. The Usertemplate Views First, we’ll create theindex view. Create the fileapp/views/usertemplates/index.rhtml, and enter the view code in Listing 12-8. C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 341 Listing 12-8. The Usertemplate Index View h1Your Blog Templates/h1 table tr thTemplate Name/th thDescription/th /tr tr td%= link_to 'blog_index', edit_usertemplate_path( usertemplates.find ut ut.name == 'blog_index' ) -%/td tdThe main template for your blog/td /tr tr td%= link_to 'blog_entry', edit_usertemplate_path( usertemplates.find ut ut.name == 'blog_entry' ) -%/td tdThe template for viewing one entry/td /tr /table This presents the user with the list of editable templates. We have used the Rubyfind method within thelink_to statement to display theid corresponding to the particular template. Clicking the template name takes the user to the edit page for that template. Create this view file now,app/views/usertemplates/edit.rhtml, and enter the code in Listing 12-9. Listing 12-9. The Usertemplate Edit View h2Editing %= usertemplate.name %/h2 %= error_messages_for :usertemplate % % form_for(:usertemplate, :url = usertemplate_path(usertemplate), :html = :method = :put ) do f % p%= f.text_area :body, :rows = 25, :cols = 80 % p %= submit_tag "Save" % or %= link_to 'cancel', usertemplates_path % /p % end % Now that our users can edit their user templates, we need to modify the entries controller to render the users’ blogs with these templates if they exist. 342 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E Rendering Liquid Templates Openapp/controllers/entries_controller.rb, and take a look at the existingindex andshow methods. Currently, we just render theindex andshow view files as normal. We now want to be able to render a Liquid user template if the user has specified one. If a user does not have any user templates defined, we should still render the regular.rhtml view. Modify theindex andshow methods as shown in Listing 12-10. Listing 12-10. The Updated Entries Controller File def index user = User.find(params:user_id, :include = :usertemplates) entry_pages = Paginator.new(self, user.entries_count, 10, params:page) entries = user.entries.find(:all, :order = 'created_at DESC', :limit = entry_pages.items_per_page, :offset = entry_pages.current.offset) usertemplate = user.usertemplates.find_by_name('blog_index') if usertemplate and usertemplate.body.any? page = Liquid::Template.parse(usertemplate.body) render :text = page.render('user' = user, 'entries' = entries, TextFilters) end end def show user = User.find(params:user_id, :include = :usertemplates) entry = Entry.find_by_id_and_user_id(params:id, params:user_id, :include = :comments = :user) usertemplate = user.usertemplates.find_by_name('blog_entry') if usertemplate and usertemplate.body.any? page = Liquid::Template.parse(usertemplate.body) render :text = page.render('user' = user, 'entry' = entry, 'comments' = entry.comments, TextFilters) end end We now have a condition in each of the methods that parses and renders a user’sblog_index template if one is available. If there is no matchingusertemplate, Rails automatically renders the normal view file. When rendering a Liquid user template, we pass the Liquid drops for the user, entry, and comment objects along with the TextFilters module that we created earlier. We can now finally try creating a new template for our blog. C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 343 Manual Testing You will have to restart the Rails application server to pick up changes to theenvironment.rb file. Do this and log in to the RailsCoders application as a regular user. First of all, try viewing your blog by going to your profile and clicking on the “See all of your blog” link to view your blog. Since you have not created a new template yet, it will be rendered using the normal Rails view files. In the sidebar menu, there is now a Blog Templates link. Click this to view the user templates available for you to edit, as shown in Figure 12-1. Since this is the first time that you have attempted to edit your user templates, the controller will have to create the empty templates and save them before showing this page. Figure 12-1. The usertemplates index view Click theblog_index template link to edit the Liquid template for the blog index page. We now need to create a blog template from scratch using the Liquid drop objects and the Liquid filters that we have been passed. ■Tip It would be a good idea to include details of the Liquid drop objects and Liquid filters available as a help page for your users. A very simpleblog_index template is shown in Listing 12-11. Enter this into the text box on the edit template page and save the template. 344 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E Listing 12-11. A Sample blog_index User Template html head title user.blog_title /title style body background: 111; color: eee; font-family:arial,sans-serif; a color: c66; /style /head body h1 user.blog_title /h1 % for entry in entries % h2 entry link_to_entry /h2 entry.body textilize % endfor % /body /html We can now take a look at your main blog page to see the new template in action. Go to your blog via your profile page or the Blogs sidebar link. The page shown will now be in a style that’s totally different from the rest of the site, as shown in Figure 12-2. Figure 12-2. The main blog view with the new user template C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 345 If you click an entry title, the entry view page will still be in the regular style of the RailsCoders site, since we haven’t defined a user template forblog_entry yet. Go back to your Blog Templates page, and click theblog_entry link. Listing 12-12 shows a simple page that lists all of the comments for an entry along with a new comment form. Listing 12-12. A Sample blog_entry User Template html head title user.blog_title - entry.title /title style body background: 111; color: eee; font-family:arial,sans-serif; a color: c66; /style /head body h1 user.blog_title /h1 h2 entry.title /h2 entry.body textilize % for comment in comments % p comment.author said:br / comment.body /p % endfor % form action=" entry.comment_post_path " method="post" p textarea cols="40" id="comment_body" name="commentbody" rows="4"/textarea br / input name="commit" type="submit" value="Save Comment" / /p /form /body /html This page simply shows the selected entry and lists all of the comments for the entry. The page also includes a basic form to allow users to add new comments. We use thecomment_ post_path filter to return the post path for the form. Save this template, and go back to view your blog. Click an entry title to view a specific entry. The page will now be in the same style as the main blog page, as shown in Figure 12-3. Make sure that the comment form path is working correctly by entering a new comment and clicking Save Comment. 346 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E Figure 12-3. The blog entry view with the new user template Now that you can see that the templating feature is working correctly, you may wish to design some better templates or create more filters to provide more features to the template authors. Testing the Usertemplates Controller We should automate the tests for the usertemplates controller using the functional test system. First of all, create usertemplates fixtures, adding three user templates, two belonging to the userid 1, as specified in the user fixtures, the other belonging to userid 2. Thetest/fixtures/usertemplates.yml file follows: valid_blog_index_for_joe: id: 1 user_id: 1 name: blog_index body: a template valid_blog_entry_for_joe: id: 2 user_id: 1 name: blog_entry body: a template valid_blog_index_for_admin: id: 3 C H A P T E R 1 2 ■ A D D I N G U S E R - C R E A T E D T H E M E S T O T H E B L O G G I N G E N G I N E 347 user_id: 2 name: blog_index body: my template Now edit the functional test file for the usertemplates controller,test/functional/ usertemplates_controller_test.rb. The full test file is shown in Listing 12-13. You will see that thelogin_as helper simulates a user logging into the application. Theindex,edit, andupdate methods are the only ones that need to be tested. We have also included two extra tests that log in as userid 1, but try to edit and update user templates that belong to userid 2. The expected result is to not allow editing of another user’s templates, so user 1 is redirected to the index page. Listing 12-13. The User Templates Functional Test File require File.dirname(__FILE__) + '/../test_helper' require 'usertemplates_controller' Re-raise errors caught by the controller. class UsertemplateController; def rescue_action(e) raise e end; end class UsertemplateControllerTest Test::Unit::TestCase fixtures :usertemplates, :users def setup controller = UsertemplatesController.new request = ActionController::TestRequest.new response = ActionController::TestResponse.new end def test_should_get_index login_as(:valid_user) get :index assert_response :success assert assigns(:usertemplates) end def test_should_get_edit login_as(:valid_user) get :edit, :id = 1 assert_response :success end def test_should_update_usertemplate login_as(:valid_user) put :update, :id = 1, :usertemplate = :body = 'a different template' assert_redirected_to usertemplates_path end 348 C H A P T E R 1 2 ■ AD D I N G U S E R - C R E A T E D T H E M E S T O T H E B LO G G I N G E N G I N E def test_should_fail_get_edit_for_other_user login_as(:valid_user) get :edit, :id = 3 assert_response :redirect assert_redirected_to :action = 'index' end def test_should_fail_update_for_other_user login_as(:valid_user) put :update, :id = 3, :usertemplate = :body = 'a different template' assert_response :redirect assert_redirected_to :action = 'index' end end Before you run these functional tests, you need to bring the test database up to date by opening a terminal window and entering the following command: rake db:test:prepare Now, run the tests with the following command: ruby test/functional/usertemplates_controller_test.rb Loaded suite test/functional/usertemplates_controller_test Started ..... Finished in 0.889032 seconds. 5 tests, 9 assertions, 0 failures, 0 errors Further Development of the User Templates This feature could be developed further in many ways: � Since developing a whole template from scratch is pretty difficult, you may wish to provide your users with a number of sample templates that they can copy into the blog_index andblog_entry templates and edit to their liking. � You will likely wish to add more filters and possibly extend the drops further, providing more tools to help your users build templates.