Learning Rails show

Learning Rails

Summary: Want to learn how to build web sites with Ruby on Rails? Learning Rails starts from the beginning and teaches all the core concepts.

Join Now to Subscribe to this Podcast
  • Visit Website
  • RSS
  • Artist: LearningRails.com, Michael Slater and Chris Haupt
  • Copyright: Copyright 2007 Collective Knowledge Works, Inc.

Podcasts:

 23: Performance Analysis with the New Relic RPM | File Type: video/mpeg | Duration: 00:50:00

Goals In this lesson, we take a look at techniques for monitoring and improving the performance of your Rails application. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the button on the left. We recommend that you right-click on the button and choose Save As to download it to your computer. This lesson does not depend on the sample application code, although you can start with the sample application code (in the Github repository) to explore the developer mode of the New Relic plugin, as we do in this screencast. Improving the Performance of Your Rails Application For most non-trivial Rails applications, there’s going to be some performance issues. Rails has been subject to a lot of performance criticism, but in reality, Rails can perform very well—it just takes some tuning to get there. Rails makes it quick and easy to get your application up and running, and in the process it also makes it easy to ignore a lot of issues that require some attention when performance is critical. Fortunately, there are plenty of tools and techniques at your disposal when it comes to performance tuning. You’ll need to look for slow queries, for queries that are being performed too frequently, and for opportunities to cache pages and parts of pages. In this screencast, which is sponsored by New Relic, we’re going to focus on the tools and service that New Relic provides. A word about our sponsorships: we think of this like public radio. Sponsors underwrite the cost of producing and distributing a lesson, and we give them opportunities to review the content. But the words and opinions are all ours, and we wouldn’t say anything we didn’t personally stand behind. New Relic is being used by a lot of folks who we’d trust to have made a good choice: 37signals (Basecamp and other apps), Twitter, Lighthouse, and Shopify, to name a few. It’s also included in all hosting packages at Engine Yard. There are alternatives to New Relic’s service, such as those from Five Runs and the recent entrant from Rubysophic. There’s related services, such as Scout, and a variety of open source performance tuning tools, such as http-perf. For more information on a variety of performance optimization tools and techniques, see the performance tuning section of the BuildingWebApps site. Installing the New Relic Plugin The New Relic RPM service connects to your application via a plug-in that they provide, called the agent. Installing it is as simple as for any other plug-in: script/plugin install http://svn.newrelic.com/rpm/agent/newrelic_rpm There’s two modes in which RPM can run. In development mode, it runs on your local development server. In either mode, the plugin hooks into the Rails framework to automatically collect performance data. In Development mode, you examine the performance data RPM collects via views that it installs into your application. In this mode, RPM stores only the last 100 HTTP transactions, and it keeps all the analysis data in memory, so it doesn’t touch your database. In production mode, it runs on your production server and sends data to New Relic’s server once per minute. New Relic’s server then provides performance reports that you can view via the web, without impacting your production server. In the screencast, we explore the basic features of the New Relic system, and then dive into a specific performance optimization example. For details on New Relic’s system, check out their documentation. We’ll start with development mode, and then take a look at a real application running in production mode. Exercise the Application With the plug-in installed in our sample application, we’ll start the server: script/server And then browse to localhost:3000. So far, this should look very familiar. Before we look at the analysis screens, we need some data to analyze. So let’s click around to several of the pages, and then log in and go to the admin interface. Viewing Performance Data in Developer Mode Now, to view the performance data, just browse to localhost:3000/newrelic. In doing so, you’re accessing a controller and views that the New Relic plug-in installed. The data is coming from the in-memory cache of information about the most recent 100 controller actions. The main RPM Developer Mode page lists the URLs for each of these actions, and the total time taken to complete each one. As you can see, nothing in this application takes very long, as you’d expect—there’s nothing here that’s doing very much work. Click on one of the URLs, and you’ll see a pie chart of where the time goes for that action. In the case of one of our simple CMS pages, about half the time is in the controller action, and you can see how the remaining half of the time is split among the rendering, database, and Active Record code. Below the pie chart, you’ll see the items listed out; click the question mark next to any of them to see a description and some tuning hints. Click the Details link to see a time-sequential breakdown of the components. The Duration column shows the total time spent in a particular method, including time spent in any other methods called by the selected method. The Exclusive column subtracts off time spent in framework code called in the method, such as ActiveRecord methods. For each line that involves a database query, you can click on the SQL link to see the query that was produced, as well as the stack trace that shows the related code. Running in Production Mode In production mode, the agent doesn’t accumulate data in memory; it sends it to New Relic’s servers. You then log in to the New Relic application, running on their servers, to view your data. New Relic has recently released a free version of its production mode service, called RPM Lite. It is limited to a couple of views, and to the last 30 minutes of data, but you can use it on any number of servers with any number of hosts (previously, the free plan was limited to a 90-day trial period and two mongrels). On a paid plan, which is priced starting at $40 per host for the Bronze subscription, you can view data from the last 7 days and you get many more views. The premium plans provide more ways to analyze the data. The Silver and Gold plans provide longer data retention of one and three months respectively and a bunch of advanced views and features, including transaction tracing, automatic incident detection, error tracking, day to day and week by week comparisons, and scalability analysis. To use RPM in production mode, you need to get a license key from New Relic, by signing up for either a paid or a free account. Your confirmation email will include a newrelic.yml file, which you should put in your config directory. There’s a variety of other settings you can tweak in that file. (You can also just copy your key from the New Relic site and paste it into the newrelic.yml file that the plugin installs.) With the agent and newrelic.yml file installed in your application, deploy it to the production server. That’s all it takes to begin collecting performance data. Viewing Production Mode Performance Data In production mode, there’s much more information available. Since it’s accumulated in New Relic’s database (currently at the rate of more than 10 billion reports a month for all customers combined), you can view data from a relatively long period of time. And the ways you can drill down into the data are much more flexible than in developer mode. Browse to http://rpm.newrelic.com, and log in with your (free or paid account) user name and password. In the screencast, we look at the live performance data for BuildingWebApps.com, and then show how we track down a missing database index, as well as the need for more caching, and then make changes to the code and verify that the performance did indeed improve. You’ll have to watch the screencast to see this information, since it is not publicly accessible. The home page lists each of the servers associated with the account. In our case, we have two production slices and one staging slice. You can define server clusters, so the production data all appears together. This “stoplight” view shows you at a glance if everything appears to be running ok. If all the lights are green, it’s good. Yellow means something is getting marginal, and red means trouble. You can define the thresholds for these color levels by clicking the “traffic light” icon next to the host or cluster name. You can click on any of the numbers next to the lights to go to a detail page. We’ll start by going to the overview page for the production server, by clicking on the Production label. From the strip at the top of any of these pages you can choose (from the three pop-up menus) which server or cluster you want to look at, which view you want, and what time period you want to examine. You can choose a period as short as 30 minutes, which lets you see a data point per minute, or extend a period as long as the last 7 days (one month for the Silver plan and three months in the Gold plan). These graphs show that our application’s CPU, memory, and database usage levels, as well as the response times, have been relatively stable, with modest fluctuations. On the Lite plan, there’s just one more view available. The Controllers view shows the overall throughput and response time graphs, plus a list of the slowest 20 controller actions during the time interval being shown. Advanced Views At the Bronze level (the least expensive paid version), you get a bunch of additional views: Page Volume: Shows the number of views for the 15 most frequently viewed URLs during the selected interval Active Record: Shows the total time spent in the database for find, save, and destroy operations, the database response time, and actions that are the top 15 database consumers Index Hunter: Shows the slowest Active Record queries, which are good places to look for missing indexes Time Consumption: The top 15 consumers of time — a good way to find the places where optimization would have the biggest impact Cluster Breakout: How performance varies for each cluster and each Mongrel Compare with Yesterday: Are things better or worse than yesterday? The Silver levels adds: Transaction Tracer: Lists all HTTP transactions that took more than two seconds; click on one to drill down into the details Errors: All Rails errors, nicely aggregated; click one for details Incidents: List of metrics that were more than 50% worse than for the same time period one week ago Week-to-Week Comparison: Compare summary metrics against one week ago And the Gold level adds: Scalability Analysis: Plots performance vs. application load Response Time Report: A configurable report that shows the sources of response time degradation Tracking Down a Performance Issue To see an example of a performance problem being tracked down and corrected, watch the screencast (right-click the button at the top left to save the video file to your computer). The tasks we demonstrate on the live BuildingWebApps.com code include: Identifying when too many transactions are happening (lots of small calls to our DB adding up). We proceed to implement a caching solution to reduce the number of calls. Identifying longer duration queries that are sometimes caused by lack of proper indicies on relevant database tables. We apply an index and see our queries change from traversing through many DB rows of data to near constant time access. We give a demonstration of the MySQL EXPLAIN command, and then show that the same information is available within NewRelic in both development and production modes.

 22: Deploying to a Public Web Server | File Type: video/mpeg | Duration: 00:42:00

Goals In this lesson, we finally deploy our application to a public web server. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the button on the left. We recommend that you right-click on the button and choose Save As to download it to your computer. The code used in this lesson has all been checked in to the Github repository. Rails deployment overview Deploying a Rails application is more complex than for a simple web site or PHP application for two reasons: In general, a separate application server is used to keep the Rails application in memory, and execute requests that come in through the HTTP server. This requires more configuration, and that the application server be restarted when the code is changed. Static and PHP sites are typically deployed by simply uploading files to the server, with FTP or SFTP. Although there is nothing about Rails that requires it, Rails applications are almost universally deployed by having the server check the code out from a version control system. (This is a better practice for all kinds of sites, but it less often used with simpler technologies.) The flow of a deployment is thus as follows: Make changes to your code locally Test the changes Check the changes in to version control (and push to the main repository, if using Git) Optionally, display a maintenance page to replace the site while it is being updated Perform a checkout of the code from the version control system to the web server Run any migrations that the new code requires Restart the application server Disable the maintenance page, if any Although this may sound complicated, with the right tools it is quite simple once you’re all set up. The key tool is a program called Capistrano, which can automate the entire process (starting with step 4). In the previous few lessons, you’ve been through the first three steps; by the end of this lesson, you’ll have been through the whole process. Choosing a web and application server configuration There’s a huge array of choices to be made when it comes to server software selection and configuration. We’ll describe one of many possible approaches in this lesson. You’ll almost surely want to explore other options, and need to learn more about issues specific to your server, to do your own deployments. Here’s where to find some useful resources on our BuildingWebApps site: Linux Rails Hosting Rails Application Servers HTTP Servers Automation We also highly recommend the book Deploying Rails Applications. For the past two years or so, the most widely used setup has been Apache 2.2 as the front-end server, which processes HTTP requests, serves static files (including JavaScript, CSS, and images, as well as any cached HTML pages) and passes (“proxies”) requests that must handled by Rails to the application server(s). Many people have started using Nginx instead of Apache as the front-end server. Whether Apache or Nginx is the HTTP server, the most widely used application server is Mongrel, which is installed as a gem. Mongrel handles only one request at a time, so most servers use two or more Mongrels (dozens for a system designed to handle high loads), and another gem called mongrel_cluster manages the Mongrels. Setting this up can be a little complex, and if you’re going to take this route, going with a hosting company that provides a “stock” configuration, and knows how to support it, is usually a good investment. A relatively new alternative, Passenger (a.k.a. mod_rails), makes all this much simpler by operating as an Apache module. Not only does it eliminate most of the configuration, it shuts down Rails processes that have been idle for a while, freeing the memory they use. This has the disadvantage of a several-second delay the first time an idle site is accessed, but it is a tremendous advantage for a server that is running lots of low-traffic sites, since they don’t always need to sit in memory. In this lesson, we’ll use Apache 2.2 with Passenger. Set up database configuration for MySQL Before we get to the server setup, we need to do a little work on our application. So far, we’ve used SQLite for our development and test databases, and the production database is set up to use it as well. MySQL is generally a better choice for production, so let’s change the production configuration. In config/database.yml, replace the section under production to the following: adapter: mysql database: learningrails_production username: deploy password: secret host: localhost The development and test databases can remain set up for SQLite. We’ve also added a migration) that will create all the pages and a couple of sample resource categories and links, so the application when deployed should look much like the version you have on your development system. This migration won’t do anything unless there are no pages defined in the database. Remember that the contents of your local SQLite database aren’t transferred to production, so you’ll need to enter, on the production system, any data you’ve added to the database on your development system (or add a migration to do so, as we’ve done for the basic site contents). This is a bit of a pain for ongoing development — you’ll probably find that you’ll want to copy the database from the production system down to development when you’re doing development work, and sometimes push it back up to production when you’ve added content locally. A GUI tool such as Navicat simplifies this process. Install Capistrano Capistrano is a powerful utility for automating any kind of web deployment. It is nearly universally used by Rails developers. First, install the Capistrano gem if you don’t already have it: sudo gem install capistrano We’ve used version 2.5 in this lesson. Some details will be different if you’re using an earlier version, and if it is much earlier, it may not work with Git. Capistrano runs on your development system, not on the server. It performs actions on the server by connecting to it via SSH and issuing commands, just as you would from an SSH terminal session. To configure the application for Capistrano, the first step is to “Capify” it. From your root application directory, simple enter in a terminal: capify . (That’s “capify”, space, period.) This adds two files to your application: Capfile at the root of the app, and deploy.rb in the config directory. We don’t need to make any changes to Capfile. Set up deploy.rb The file deploy.rb is where you tell Capistrano how to access your code repository, as well as other details of your production server(s). The art of configuring Capistrano is mostly a matter of setting up the deploy.rb file. There’s a guide at github that gives us some guidance about what is needed so Capistrano can fetch our code directly from our github repository. There’s also a lot of tutorial content at capify.org. Here’s the lines we need to add to support checking out from our public Git repository: default_run_options[:pty] = true set :repository, "git://github.com/chaupt/learning-rails-sample-app.git" set :scm, "git" set :branch, "master" set :deploy_via, :remote_cache The first line ensures that password prompts are passed through. The next three lines tell Capistrano where the repository is, that it is a Git repository, and which branch to use. The last line tells Capistrano to keep a cache of the repository on the web server, so it only has to transfer new code from the repository when deploying a new version. If you are deploying from your own repository, you’ll need to change the repository URL, and if it isn’t a public repository, you’ll want to set up an SSH key pair, with the private key on your server and the public key added to your Github account; see the help docs on Github for details. Then we need to tell Capistrano about our servers: set :application, "learningrails" set :deploy_to, "/var/www/apps/#{application}" set :user, "deploy" set :admin_runner, "deploy" role :app, "sampleapp.learningrails.com" role :web, "sampleapp.learningrails.com" role :db, "sampleapp.learningrails.com", :primary => true The application name is used to name the folder into which the application code is deployed. The second line allows us to specify where this folder is located on the system, with the application name substituted in as a variable. Then we tell Capistrano what user name to use when logging into the server. (It is customary to use a dedicated user name just for deployments.) By default, Capistrano uses sudo to execute commands on the server. This won’t work on most shared servers, where you don’t have sudo access, so you might need to add the option set :use_sudo, false. If you do so, however, Capistrano might not be able to create the directories for your application. We’ve left sudo enabled, and used the admin_runner variable to set the user name under which Capistrano will use the sudo command when creating directories. This is what worked best for us in our server configuration, but your mileage may vary; if in doubt, check with your hosting company. (Versions of Capistrano prior to 2.4 behave somewhat differently.) The three “role” lines tell Capistrano about our servers. We’re using a very simple configuration, in which the web (HTTP), application (Rails), and database (MySQL) servers are all on the same machine, but they don’t have to be. You can also have a cluster of servers, with more than one server performing each role, and Capistrano will take care of, for example, checking out a copy of the code to each application server, and modifying the setup on each web server to display a maintenance page. Finally, you need to tell Capistrano how to start and restart the application server. There are standard Capistrano tasks for this that are designed for Mongrel and similar application servers; for Passenger, things are simpler, but we need to tell Capistrano just what to do, with these lines of code: namespace :deploy do desc "Restart Application" task :restart, :roles => :app do run "touch #{current_path}/tmp/restart.txt" end desc "Start Application -- not needed for Passenger" task :start, :roles => :app do # nothing -- need to override default cap start task when using Passenger end end The namespace block tells Capistrano what name prefix to use for these tasks. Namespaces help avoid unintentional name conflicts in complex setups. To restart Passenger, you only need to update the modification date of the restart.txt file, which is done with the touch command. Note that you can simply write run and then the command, and Capistrano knows to open an SSH terminal to the app server (as specified on the task line) and issue this command. current_path is a variable that provides the path to the current version of the application. With Passenger, you don’t need to do anything to start the app server, but we need to define an empty start task to override the one that is part of the default Capistrano tasks. These tasks give you a hint of what’s possible with Capistrano — you can use it to automate nearly everything you’d want to do on your server. Keep in mind, though, that if you use the :use_sudo, false setting, you can’t do things that require a privileged user. We’re done changing code now, so if you’re working in your own repository, commit your changes now. We’ve already made all these changes in the public Github repository. Install Passenger Now we’re ready to set up the server. There’s a vast array of options here, depending on whether you’re on a shared or dedicated server, what operating system is installed, and so forth. To avoid a lot of system-specific discussion and keep this lesson to a reasonable length, we’re going to assume you have a Linux server with Apache 2.2, MySQL 2, and Git installed. If not, see the links at the start of this article for pointers to some resources, or check with your hosting company. To install Passenger, simply enter in an SSH terminal window connected to your server: sudo gem install passenger And then: passenger-install-apache2-module The Passenger installer will check for any required dependencies and give you specific instructions if there’s anything else you need to set up first. Configure Apache Now we need to configure Apache. Your system should have a base Apache configuration already, often in /etc/httpd/conf/httpd.conf, though this varies depending on the Linux variant and system configuration. We need to add a few lines to the Apache configuration file to tell Apache to use the Passenger module: LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3 PassengerRuby /usr/bin/ruby The Passenger installer will provide these lines for you; the paths and other details may be different for your system configuration. Then we need a virtual host configuration for our site. We put this is a separate file, which is included by reference at the end of the main Apache config file. Here’s what we used: <VirtualHost *:80> ServerName sampleapp.learningrails.com DocumentRoot /var/www/apps/learningrails/current/public <Directory "/var/www/apps/learningrails/current/public"> Options FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory> </VirtualHost> The first line identifies the root URL for your site. We’ve used the URL for our demonstration server, but you’ll want to use your own server URL here. You’ll need to set up a DNS record to point this name to the IP address of your server. The second line must point to the public directory of your Rails application. We told Capistrano to put the application in /var/www/apps/learningrails, and as we’ll see shortly, current is a symlink that points to the currently active version. The Directory directives ensure that Apache is allowed to access the public directory. This may or may not be necessary, depending on what the rest of your Apache config file looks like. Now restart Apache, and make sure there are no errors reported. On our server, this can be done with: sudo /sbin/service httpd restart Once again, the command may be different for your server configuration. Set up MySQL We have just one more thing we need to do on the server: create the empty database. You can do this with a MySQL GUI application, if it can connect to your server (typically using an SSH tunnel), or you can do it from the command line in a SSH terminal session. To use the latter approach, first create the database with the following command at the shell prompt: mysqladmin -u root -p create learningrails_production This command assumes that you have a user configured for MySQL named root, and you’ll be prompted for root’s database password after you enter this command. Then, start the mysql monitor program by entering mysql -u root -p and create the deploy user by entering the following command at the mysql prompt: mysql> grant all privileges on *.* to 'deploy'@'localhost' identified by 'secret'; (Note that you don’t enter mysql>; that’s the prompt.) The user name (deploy) and password (secret) must correspond to those in your database.yml file’s production section. Your first deploy Now, on to Capistrano tasks. First, to get an overview of all the Capistrano commands available, enter (in a terminal on your development system): cap -T Remember, Capistrano always runs on your development system, even though it is issuing commands to your server. Now there’s some one-time-only setup: cap deploy:setup Capistrano will connect to your server, and will prompt you for the server password if don’t have an SSH key set up. Capistrano will then create some directories to hold your application code. To make sure everything is ok, enter: cap deploy:check Hopefully, this tells you that everything looks good; if not, read the error messages carefully and correct any problems. If there are permissions issues, Capistrano may not be able to create the directories it needs. You may need to log into the server and create the /var/www directory manually. For our server, the owner of this directory is set to deploy, and the group to apache. Now, just enter: cap deploy:cold And you should see lots of lines fly by, as Capistrano tells the server to check out the code from Github and sets up some symlinks. At the end, all the migrations should be run. If all has gone well, you should now be able to browse to your server and see the application running. (You can visit http://sampleapp.learningrails.com to see the copy that we deployed.) Troubleshooting There’s a lot that can go wrong in this process, and unless everything works, your app won’t. So watch the Capistrano scripts carefully while they’re running. If there were no errors there, but it hangs on checking out the code, then there’s a problem with the authentication with Github (if you’re using a private repository) or the Git URL. If you get no response when you try to access the application, make sure your DNS is set up correctly and has propagated to your computer, and that Apache is running. The Apache error log file may be helpful (the location varies depending on the system configuration; on our system, it is /var/log/httpd/error_log). If you get a Rails error page (error 500), then check the Rails log on the server for clues (/var/www/apps/learningrails/shared/log/production.log). Because of the wide range of issues that can come up, it’s unlikely that we’ll be able to answer all your questions here. Here’s some resources to try: Google group for Capistrano Google group for Rails Deployment Google group for general Rails issues Google group for Passenger RailsForum LinuxQuestions forum And, of course, there’s always the tutorials, FAQs, and support staff at your hosting company. Deploy, and deploy again Once this is all set up and working, deploying new versions is gloriously simple. First, be sure to commit your changes and push them to Github. Then, just: cap deploy Or, if you’ve added any migrations: cap deploy:migrations If you want to put up a maintenance page during deployments, first issue this command: cap deploy:web:disable And then do the deployment. When it’s done, just: cap deploy:web:enable Something wrong? Just roll back Another great thing about Capistrano is that it maintains multiple versions of your application on your web server. In the directory /var/web/apps/learningrails, you’ll find three directories: shared, releases, and current. shared is for information that doesn’t change with each deployment. Log files, for example are stored in shared/log. If you have assets that are uploaded to the server and don’t go in the database or the repository, then they should go here, and you’ll want to add a Capistrano task (which can be automatically executed after each deployment) to symlink to the shared location from the current public directory. releases has a directory for each deployed version. By default, Capistrano keeps the most recent four versions. current isn’t a real directory; it’s a symlink to the current release directory. So you can always refer to the current version as /var/www/apps/learningrails/current, and you’ll really be accessing one of the directories inside releases. So what’s the use of all these releases directories? If you deploy a version and then find that your application has a serious problem, just roll back: cap deploy:rollback This task takes only a moment to execute, because all it has to do change the current symlink to point to the previous releases directory. And there’s more… We’ve just scratched the surface in this lesson, as deployment is a very complex topic. There’s much more you can do with Capistrano, and more that you should know to create a secure production deployment. Check out the links early in this lesson page to dive deeper. Next lesson, we’ll look at how to evaluate the performance of your deployed application and find performance troublespots using New Relic’s RPM service.

 21: Version control with git | File Type: video/mpeg | Duration: 00:24:00

Goals In this lesson, we put our application into a version control system, in preparation for deploying the application in the next lesson. We do this first because the proper way to deploy an application is not to copy it from your computer to the server, but to have the server pull the application from the version control system. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Choosing a version control system There are many version control systems. CVS was the first system widely used in the open-source community. Several years ago, it was largely replaced by Subversion. And in early 2008, most of the Rails world moved to a newer version control system, called git. We’re going to use git in this lesson, since it has become the standard in the Rails community, and is used for Rails itself and most plugins. git was originally written by Linus Torvalds to manage development work on the Linux kernel. It’s an extremely capable system, with more than 100 commands. We’re going to just scratch the surface of its capabilities in this lesson, but you’ll learn enough to use it for day-to-day development. To make full use of it, spend some time with the resources listed on our git page. In particular, you may want to purchase the PeepCode git screencast. If you’re familiar with other version control systems, you probably expect there to be one central repository stored on a remote server somewhere. git is different; it is a distributed version control system, in which there can be any number of repositories for a single project. You’ll create a local repository on your computer, and if you want, you can create a remote repository elsewhere, such as on a hosted git service like github.com. (You’ll need to have a repository that is accessible from the Internet to deploy your application.) Setup We aren’t making any changes to the code in this lesson, and you don’t need to have the code installed locally to complete the lesson. If you follow along, by the end of the lesson you’ll have your own copy of the code on your computer, managed by a local git repository, and you’ll have your own repository stored on github as well. First we need to install git. Assuming you’re on a Mac and have Macports installed, open a terminal and enter: sudo port install git-core Now set up your user information in the git configuration: git config --global user.name "Michael Slater" git config --global user.email learningrails@buildingwebapps.com See our articles on installing Ruby on Rails for more details: Setting up Rails on Leopard Setting up Rails on Tiger Setting up Rails on Windows Vista Setting up Rails on Windows XP Creating a git repository for an application on your computer There’s two paths you could take to creating a repository on your computer: If you have an application already on your computer, you can create a local git repository and add the application to it If you want to start from an existing application, such as the Learning Rails sample application, you can make your own copy of that repository. In this lesson, we’ll do both of these things. First, we’ll show how we put the sample application into a git repository and then created a public repository on github. Then we’ll show how you can make your own version of that repository. Change to the root directory for the application and enter: git init This creates the .git folder at the root of your application, where git stores all of its information. Telling git to ignore files we don’t want it to track We don’t want git to track our log or temp files, so we need to create a file call .gitignore at the root of the application. If you’re using textmate, you can enter in the terminal: mate .gitignore And then enter the following lines into this file: *.log tmp/**/* .DS_Store The first line tells git to ignore any files that end with the .log extension. The second line tells it to ignore the temp directory contents (individual files as well as subdirectories), and the last line tells it to ignore the OS X housekeeping hidden file that is in each directory. (If you’re on Windows, you might want to replace this with Thumbs.db.) Now there’s one more little complexity with the ignore. Since we’ve told git to ignore all .log files, it sees the log directory as empty, and git doesn’t track empty directories. As a result, the log directory would not be put in the repository, and if you just checked out the files, you’d get an error on starting the application because Rails wouldn’t be able to create the log file. So we put a dummy file into the log directory, so git will not see the directory as empty and will put it in the repository. touch log/.gitignore Adding files to our local git repository Now we’re ready to add files to the repository. From the root of the application, enter: git add . which tells git to add all the files (except those excluded by our .gitignore file). Note that this doesn’t actually commit the files to the repository; it just adds them to what git calls the index, which lists files that are ready to be committed. You can view the list by typing: git status Now we can commit the files to the local repository by typing: git commit -m "Initial commit" The -m option allows us to specify a commit message, which is required. (If you leave off the -m option, git will try to open an editor for you to provide the commit message, but this will only work if you have the proper environment variable set up.) Setting up a github account We now have our application in our local repository. At this point we have all the benefits of version control, but we still want to copy this to a remote repository. There’s two reasons for this: first, as a backup, and second, so that other contributors, and most immediately, our deployment code (which we’ll get to in the next lesson) can access it. First, we need to create an account on github. We’re going to create a temporary account for the purpose of this lesson. You’ll need to create your own if you want to have your own repository there. Visit www.github.com and click the Pricing and Signup link at the top. You can choose the free account as long as you’re willing for your code to be public; if you want a private repository, you’ll have to choose one of the paid accounts. Setting up your SSH key You’ll need an SSH key to use your repository. If you already have a key pair you’ve made for other purposes, you should be able to use your existing public key. If not, go ahead and create one now. For details on creating an SSH key, see the github guide on SSH keys. If you’re on Windows, you may also find our article on SSH with Windows to be useful. Whether you’re using an existing key or created a new one, paste the public key into the signup form. Push to the remote repository The following command tells git that we want to add a remote repository definition, called origin by default, and provides the URL to that repository. (Note that we’ve shown the address for our learningrails repository at github here; you won’t be able to push to this repository, since you don’t have write access rights. We’ll show how you can push your version to your own remote repository shortly.) git remote add origin git@github.com:learningrails/learning-rails-sample-app.git And now we can copy to the remote repository with the command: git push origin master This tells git to push all changes in the local repository to the remote repository named origin, and to use the master branch (which in our case is the only branch that exists). When this completes, we have synchronized our local and remote repositories. Making our own repository to hold your own version of the Learning Rails code Now that you’ve seen how to create a repository from scratch, let’s make your own fork of the Learning Rails repository so you can use it to maintain your own version. In the next lesson, we’ll show how to deploy your application from that repository. First, you need to create an account at github, if you haven’t done that already, and log in. Once your account is set up, browse to http://github.com/mzslater/learning-rails-sample-app/tree/master (or search for learning-rails-sample-app from the github search facility, but make sure you choose the version whose owner is mzslater). Click the Fork button, and github will create a repository that is a duplicate of our repository, and it also keeps track of the relationship between the two repositories. Cloning your repository You now have your own repository! The next step is to clone it on your local machine. On your computer, open a terminal and change to the directory in which you want to place your code. We use a directory called “development” in our user directory. You don’t need to make a subdirectory for the application; git will do that for you. On your repository page on github, you’ll see the clone URL listed, and if you click it github will display the full command that you need to enter on your computer to create the clone. It will be something like: git clone git://github.com/accountname/learning-rails-sample-app.git where accountname is the name you chose when you created your github account. Run this command, and if all goes well, you’ll have a local clone of your github repository. If you get a permission denied message, you probably have a problem with your ssh key. Check this page for some pointers. Changing your version of the code Now make some edit to your local code, so you can test the process of committing changes and pushing them up to the github repository. After making the change, enter the command: git commit -a -m 'make up a commit message here' Your change is now committed to your local repository, but the github repository hasn’t yet been updated. To push the change up to that repository, enter the command: git push origin master Now you can visit your repository at github, and it will show the commit. You can click on the commit to see the changes that were made, and use the source code browser to see all the code. If you make improvements to the code that you think would be of interest to others, click the Pull Request button on your repository page to send us a message telling us what you’ve done, and we’ll consider adding it to our repository. Please be specific about describing the changes you’ve made and why they are useful to everyone. Coming Up In our next lesson, we’ll explore the Capistrano deployment utility and deploy the code from the github repository.

 20: Testing your site (Part 2) | File Type: video/mpeg | Duration: 00:15:00

Goals In this lesson, we finish up our testing journey by fixing up some of our functional tests. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup The code in this episode is the same as the ending state of the previous episode and contains all of the fixed tests. Learning Rails example app code as of the end of this lesson Functional Tests We continue where we left off, having working unit tests but many broken functional tests. Fixing the Functional Tests We now know how to read the test results, so switching our attention to the functional test block results, we see we have our work cut out for ourselves: FEEFFEEFEEFFEEFFFFEFFFFFFFFF.........FEEEE. Which in my first run was equivalent to 43 tests, 36 assertions, 20 failures, 13 errors. Ouch. The Rails scaffold generator does a much better job at creating functional tests than it does with unit tests. Functional tests exercise our controllers. Peeking inside most of the test files in the test/functionals directory, you will note that they follow similar patterns. So what went wrong? In scanning through the test diagnostics, we see a lot of tests with similar issues. There are many tests with problems with redirects: “Expected response to be a <:success>, but was <302>”. There are many tests where things were not created, updated, or destroyed when expected. Thinking back, the common thread here is that all of these things need you to be logged in, yet, when the tests are automatically generated for us, the generator has no idea about this requirement. Let’s fix that. The restful_authentication plugin that we used provides a utility module called AuthenticatedTestHelper. This module contains useful methods that you can mix-in to your test classes to simulate things like logging in, precisely what we need. Since we protected most of our administrative functions with those before_filter :login_required checks, that will cause any of our tests that try to directly exercise an action to instead get redirected to a login screen. Let’s pick one of our controller’s tests to fix up as our example. The others will follow similar patterns and you can look at the final project code to see the changes (or try to make the changes yourself as an exercise). First, add the authentication system’s helper at the top of the test class. While there, add the user fixtures so we have some test user accounts to log in with: class CategoriesController < ActionController::TestCase include AuthenticatedTestHelper fixtures :users Now, let’s use the new method we have to log in as the user quentin: def test_should_get_index login_as :quentin get :index assert_response :success assert_not_nil assigns(:categories) end Run the tests again. The redirect failure on test_should_get_index should now be gone. Go back to each method in this test class and add the login_as :quentin line at the start of each test method. All of your redirect failures should now be cleaned up. But, there are still failures to fix. Look through the other functional tests and make the same changes to all of the others that are protected with the before_filter (links_controller_test, messages_controller_test, pages_controller_test, users_controller_test). For the controllers that are selective about the login requirement, be sure to not login when it isn’t needed. For instance, @MessageControllerTest@’s new and create tests are not protected by login. See the controller to confirm this. Run the tests again. We are getting closer. In my run, the results look like this: Started FEE..EEFEE.E.EEF...E.F................FEEEE. Finished in 0.547902 seconds. Our CategoryControllerTest is responsible for a number of those errors and failures, so let’s clean it up next. test_should_create_category fails with a pattern that will become familiar to you as you test code that uses validations. Here, the code is trying to make a new category object. The test, however, by default tries to create an empty category. Looking inside of the Category model, we see a validates_presence_of on :title. We better fill in a value for the title: def test_should_create_category login_as :quentin assert_difference('Category.count') do post :create, :category => { :title => 'test' } end assert_redirected_to category_path(assigns(:category)) end Go through the rest of the code and find and fix the various create tests that need values. Some test methods seem to be using fixture data, note the categories(:one) syntax we see in methods like test_should_show_category: def test_should_show_category login_as :quentin get :show, :id => categories(:one).id assert_response :success end   The generator script preloaded the fixtures with the test data it created when we started. Since we renamed the IDs in our fixture files, we need to tweak all instances of fixture usage to use valid ID symbols: def test_should_show_category login_as :quentin get :show, :id => categories(:ruby).id assert_response :success end Go through all of the test code and fix up renamed fixtures now. Writing a new functional test There are places where we added methods to our RESTful controllers. For instance, in the LinksController we added the list action. We add a test for it in LinkControllerTest: def test_should_get_list get :list, :name => 'pageone' assert_response :success assert_not_nil assigns(:categories) assert_not_nil assigns(:pagetitle) assert_not_nil assigns(:page) end In this example, we pass a parameter to the list action. list expects the name of the page so it can be passed through to the get_page_metadata helper we wrote, which itself loads up page and pagetitle instance variables to be used to set the page title and appropriate tabs in our navigation interface. To make this work, we have to include the pages fixture to LinksControllerTest and we tweak the fixture file itself so the with the id one has a name value of “pageone”. (See the source code if this is unclear.) We apply the same fix to the @MessagesControllerTest@’s test_should_get_new method. While we are looking at MessagesControllerTest, we see one last failure on the update action. It is expecting a redirect (proper behavior on an update) but instead is getting a 200 response. Looking at the code for this action, we see that if validation fails when updating attributes, the action simply displays the edit form again, and hence we get an HTTP 200 code. Peeking inside of the default fixture data for messages, we see the problem. Change the email fields to be properly formatted: one: name: MyString email: bob@example.com company: MyString phone: MyString subject: MyString body: MyText two: name: MyString email: two@example.com company: MyString phone: MyString subject: MyString body: MyText Final cleanup We lost a valuable piece of information when we fixed all of those authentication tests. We really should also test that the right thing happens when someone is not logged in. Let’s write a quick example of a test that succeeds when an unauthenticated person tries to get a protected resource. In LinkControllerTest, add this test: def test_should_not_get_index_not_logged_in get :index assert_redirected_to new_session_path end Here, we are not logged in, and we are trying to get the index of links. We should not be able to do this, and we expect to be redirected to the log-in screen. The assert_redirected_to new_session_path uses the RESTful route to the session controller’s new action. This is where login is initiated. Run the test, and success! Further Exercises Install the ZenTest gem and take autotest for a spin (gem install ZenTest). This will continually run your tests as you code and save files. A real time saver. Install tarantula to explore integration testing and data fuzzing See our Testing topic for many other articles

 19: Testing your site (Part 1) | File Type: video/mpeg | Duration: 00:39:00

Goals In this lesson, we’re going to step back and look at a subject we’ve neglected: Testing. First, we’ll take a brief tour to understand the support Ruby and Rails provides us for using tests in our regular development. Then, we will take stock of the current status of our code and make sure it is on solid footing with testing. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 18. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the start of this lesson Learning Rails example app code as of the end of this lesson A Brief Tutorial on Testing Why Write Tests? In our previous 10 screencasts, we’ve built a simple Ruby on Rails site. To move along as fast as possible, we’ve neglected testing, and in this lesson, we’re going to remedy that. Tests provide a safety net that helps you improve the quality of your code. You can certainly write significant Rails applications without writing tests — I have to admit I’ve done so myself, and I suspect that many Rails developers are continuing to do so. Rails doesn’t force you to write tests. But it is one of the hallmarks of professional-quality code that it has complete tests. In the short term, writing tests adds one more thing you need to do, and another set of syntax and technology you need to learn. There’s no doubt that this takes more time up-front. But in the long run, writing tests can save you time, since problems are easier to track down when they do occur. Many developers advocate writing tests even before you write the code, which forces you to think through exactly what your code is supposed to do. Tests are helpful in finding the corner cases that you might not remember to test manually, and in being able to automatically test all aspects of your code whenever you make a change. Without tests, it’s common for a change you make to fix one thing to accidentally break something else, which you might not notice right away. Tests are especially valuable when you’re doing major rework on your code, commonly called refactoring. With a good suite of tests, you can be confident after you’ve rewritten some code that it hasn’t broken anything. Testing Background Many studies have shown that the cost of fixing bugs rises with time. Imagine if you had a “quality safety net” that helped protect you by checking basic assumption about your code and notified you if something broke those assumptions. If you had such a tool, it would give you greater confidence as you added or refactored code in your program. The Ruby on Rails community has adopted many so-called Agile practices, and code testing is but one. The core Rails team has made it very easy to implement testing in your program by building in simple testing tools in the standard Rails installation. Whether you choose to write tests after coding parts of your program (“Test After Development” – TAD) or before you write a line of code (“Test First or Test Driven Development” – TDD), Rails will accommodate you. Ruby on Rails has built in support for a variety of testing scenarios. Rails currently uses the Ruby Test::Unit library to implement three kinds of tests: Unit, Functional, and Integration Tests. Rails’ usage of the names for testing terms is slightly different than common meaning. In Rails, Unit tests are tests that exercise Model objects. Functional tests focus on testing controllers. Integration tests are meant to test multi-step workflows that trigger one or more actions, potentially across multiple controllers. A fourth type, “acceptance tests” is sometimes used to mean tests that exercise workflows from a user perspective, usually by automatically triggering actual view code. We’ll skip that in this screencast. Testing in Rails When you generate a new Rails application, you will note that a test directory is created. Inside of test, you will see a number of directories: fixtures, functional, integration, mocks, and unit. Unit, functional, and integration directories match up with the test types we just defined. The fixtures directory is used to store test data that can be automatically loaded inside of test code to simulate a known state of your program. Commonly, fixtures are written in YAML, although they can be stored in other formats and can also use Erb to implement dynamic data generation. Mock objects are objects that stand-in for real code, replacing expensive (resource-wise), external, or state-changing code. The mocks directory was originally intended as a place you could drop code that would replace other code in your project during testing. This practice is largely deprecated in favor of using external libraries such as “Mocha” or “FlexMock”. We’ll passover this are in this screencast. When you use one of the script/generator scripts for making models, controllers, or even scaffold, they will generate pre-populated test code in the proper directory. You can make your own test files too. A test is simply a class that is derived from one of several parent class that Rails provides. Parent classes implement functionality appropriate for the kind of test, e.g. ActionController::TestCase sets up an environment that allows you to simulate calling a controller. Behind all of these Rails provided classes is the Ruby library called Test::Unit. A test class is made up of one or more methods. Each method examines one aspect of the object under test. The checks we do in a test method are called “assertions”. The testing framework provides a wide variety of helper methods that we can use to write assertions. Most assertions boil down to checking whether some condition is true or false. For instance, in our links_controller_test.rb file, we can find a test that checks whether we can get the new action’s form: def test_should_get_new get :new assert_response :success end We see one assertion using the assert_response helper method. assert_response examines the HTTP response object that comes back from doing an HTTP GET on the @LinkController@’s new action. We are asserting that the response code will be a success code, such as 200. We’ll see many different kinds of assertions as we write tests. You will note that certain naming convention are in play for tests, just like elsewhere in Rails. Test files end with the suffix “_test” and test methods inside of these files start with the “test_” prefix. As we look inside of the test subdirectories, we’ll see a lot of files that have been generated as we’ve worked on our CMS project. How do we use these tests? Running Tests Rails provides a complete environment for running your tests. Look inside the config directory and you will see an environment/test.rb file. This sets up testing specific options within the Rails framework to maximize the amount of testing checks that occur. Database.yml specifies a separate test environment as well. The test oriented database keeps your test data separated from your development or production data and allows the test tools to set up pristine testing conditions as needed. You can ascertain your test database is ready to go with the rake db:test:prepare command. Let’s do that now. Ruby and Rails provides a number of ways to run our tests. The easiest manual way is to fire up the rake test command. This command will run all of our tests: unit, functional, and if present, integration. We can run a subset of our tests by specifying the type, such as rake test:units. Let’s take a look at how the project is fairing at the moment: rake test This runs our tests and we see a lot of diagnostics go by. Let’s examine a few critical pieces of information. First, a test can be in one of three states: Success, indicated with a period (.), sometimes also called “green”; Failure, indicated with an F, (called “red”), which means one of the assertions we wrote to test an assumption about our code did not work; Error, indicated by an E, (also called “red”), which means a program logic problem was detected in our test or application code. The rake test results print out a sequence of progress characters for each test run: ....FFFF....E... Second, each test that fails or errors out will display relatively detailed information about what went wrong. This is your first line of defense to track the program down: 3) Failure: test_should_get_edit(CategoriesControllerTest) [./test/functional/categories_controller_test.rb:30:in `test_should_get_edit' /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/testing/default.rb:7:in `run']: Expected response to be a <:success>, but was <302> The testing environment in Rails also has its own log file, so you can peek inside of log/test.log for more information too. Armed with this information, let’s start cleaning up our CMS program’s broken tests. Cleaning up the tests Unit Tests We are working through our tests type by type. First up is unit tests and on running rake test, generally it looks good, only one problem, right? Yes and no. The error comes from our mailer test, which we’ll fix shortly. It turns out that the other model tests are largely empty. We’ll demonstrate adding a test to one of these files and leave it as an exercise to you to fill in more tests. Fortunately some plugins, such as the restful_authentication plugin we used, generate their own tests, and our user and session models start out with some useful tests that you can examine. Take a peek at user_test.rb. Lots of good tests for verifying that the User model is working. Let’s look at the tests for our Message model, since it is used by our mailer. The default scaffold generator creates a test file that contains an empty test: require File.dirname(__FILE__) + '/../test_helper' class MessageTest < ActiveSupport::TestCase # Replace this with your real tests. def test_truth assert true end end That isn’t too useful. Recall that the Message model does some validation. Let’s test those validators: class Message < ActiveRecord::Base validates_presence_of :name, :subject, :body validates_format_of :email, :with => /^(\S+)@(\S+)\.(\S+)$/ end We add a variety of tests in MessageTest: def setup @message = Message.create(:name => 'Bob', :email => 'bob@example.com', :company => 'Acme', :phone => '123.456.7890', :subject => 'test subject', :body => 'please test me') end def test_valid_model assert_valid @message end def test_missing_required_attributes assert_equal false, Message.new.valid? end def test_requires_name @message.name = nil assert_equal false, @message.valid? assert_equal "can't be blank", @message.errors[:name] end def test_requires_subject @message.subject = nil assert_equal false, @message.valid? assert_equal "can't be blank", @message.errors[:subject] end def test_requires_body @message.body = nil assert_equal false, @message.valid? assert_equal "can't be blank", @message.errors[:body] end def test_does_not_require_phone @message.phone = nil assert_valid @message end def test_poor_email_formatting @message.email = 'spammer-no-domain' assert_equal false, @message.valid? assert_equal "is invalid", @message.errors[:email] end The setup method is called before each test method is run and sets up a known correct example of our Message object for us to use. We check whether the object is valid, we see that a new empty Message is not valid (it fails validation on our required attributes), and proceed to test our required attributes and email formatting rules. We can use a different technique for setting up test data by using fixtures. First, lets set up the fixture files links.yml: learningrails: url: http://learningrails.com/ title: LearningRails podcast home page description: Pod and screencast dedicated to learning the Ruby on Rails framework categories: ruby, rails google: url: http://google.com/ title: Google description: Widely used search engine categories: search and categories.yml: ruby: title: Ruby description: Ruby the Programming Language links: learningrails rails: title: Ruby on Rails description: Rails is a cool framework links: learningrails search: title: Search Engine description: Used to find things on the web links: google Fixtures are smart about associations. We can specify attributes and associations. The associations are set up using the name of the association, then the id of the related fixture. The id is simply the first non-indented word of each YAML block. When the test code loads up these fixtures, it automatically sets up the associations. Now we can use these fixtures in our link_test.rb file: require File.dirname(__FILE__) + '/../test_helper' class LinkTest < ActiveSupport::TestCase fixtures :categories, :links def setup @link = Link.create(:url => 'http://buildingwebapps.com/', :title => 'BuildingWebApps.com', :description => 'Resource for Web Developers') @category = Category.create(:title => 'Programming', :description => 'All about programming') end # Replace this with your real tests. def test_valid_model assert_valid @link end def test_valid_from_fixture assert_valid links(:learningrails) end def test_has_categories link = links(:learningrails) assert_valid link assert !link.categories.nil? assert_equal 2, link.categories.length end def test_add_category assert_valid @link assert_valid @category assert @link.categories.empty? assert @category.links.empty? assert_difference "@link.categories.length" do @link.categories << @category end @link.reload @category.reload assert !@link.categories.empty? assert !@category.links.empty? assert_equal 1, @link.categories.length assert_equal 1, @category.links.length end end   Note the fixtures call near the top. This explicitly tells the test code to load the named fixture files into the test database before each run of the tests. Like before, we also create a couple of hard-coded objects to use in some of our tests. test_valid_model is exactly like before. test_valid_from_fixture demonstrates how you can reference an instance of a fixture data object instead. Here we refer to the links fixture data with the id :learningrails. The rest of the tests exercise the has_and_belongs_to_many association. We’ll leave testing the other model’s to you. Before we leave unit tests, though, let’s fix up the mailer test, since it is a little different. 1) Error: test_message(ContactMailerTest): NoMethodError: undefined method `subject' for Thu Jun 26 14:53:52 -0700 2008:Time /Users/chaupt/Documents/Business Documents/CollectiveKnowledgeworks/Podcasts/sc11/learningrails_19/app/models/contact_mailer.rb:4:in `message' /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:410:in `__send__' /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:410:in `create!' /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:403:in `initialize' /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:351:in `new' /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:351:in `method_missing' ./test/unit/contact_mailer_test.rb:10:in `test_message' /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/testing/default.rb:7:in `run' 18 tests, 30 assertions, 0 failures, 1 errors This gives us a clue as to what went wrong and where. The ContactMailerTest class’ test_message method had a programming error in it: an undefined method ‘subject’. This test is exercising the pseudo model that was created when we generated our contact mailer. The test inside of the test class looks like this: def test_message @expected.subject = 'ContactMailer#message' @expected.body = read_fixture('message') @expected.date = Time.now assert_equal @expected.encoded, ContactMailer.create_message(@expected.date).encoded end   Mailer related unit tests are a little strange. What this code is trying to do is test to see if the mail message that gets generated by the ContactMailer.message method is identical to a canned email fixture. @expected is an instance variable created for us by the testing class that embodies a TMailer object and which we’ll load with data and subsequently ask to generate a properly formed email body and headers (that is what encoded does). Here we see that the other special calling convention on a mailer is being used: create_message. Like deliver_message, this method uses the message method we wrote, but rather than building a mail message and then actually emailing it out, this form just returns a string that contains the body of the email message. To fix this test, we are going to take a different approach. Since we generate a multipart email (plain text and html), we’ll generate a mail message then probe it for correct results. We’ll use Ruby’s inline document string notation to specify a test value for our message body (rather than a fixture file). Note we could put some of this in a setup method like before, but in versions of Rails before 2.1, setup was slightly broken for mail tests: def test_message # using a the setup method is not functioning properly for Rails prior to 2.1. This should # rightly be put in such a setup file @message = Message.new(:name => 'Bob', :email => 'bob@example.com', :company => 'Acme', :phone => '123.456.7890', :subject => 'test subject', :body => 'please test me') test_body = <<EOF Email from your web site From: Bob Company: Acme Phone: 123.456.7890 Message: please test me EOF created = ContactMailer.create_message(@message,@expected.date) assert_equal 2,created.parts.size assert_equal "multipart/alternative", created.content_type assert_equal "text/plain", created.parts[0].content_type assert_equal "text/html", created.parts[1].content_type assert_equal test_body, created.parts[0].body end We save and run this now, and Success! Our admittedly mostly empty unit tests are all running in the “green” state. Let’s go on to fix up some of our functional tests. Wrap up We will continue testing in the next episode where we focus on the functional tests.

 18: Adding a Contact Form and Mailer | File Type: video/mpeg | Duration: 00::00

Goals In this lesson, we’re creating the “Contact Us” page. There’s two major parts to this: creating the message model and the associated forms and admin setup, and then creating a mailer that takes new messages and sends them to the site administrator via email. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 17. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the start of this lesson Learning Rails example app code as of the end of this lesson Creating the contact form Scaffolding the Message Model If we only wanted to send an email when the contact form was filled in, we wouldn’t really need to use an Active Record model and save it to the database. But it’s actually easier to use all the scaffolding and other support that Active Record provides, and it is handy to have the messages stored in the database so the can be reviewed independently of email. We start by creating a scaffold for contacts: script/generate scaffold message name:string company:string phone:string email:string subject:string body:text This generates the model, controller, views, and test files. Now run the migration: rake db:migrate And start the server: script/server Hooking up the Contact Form First delete the layout file the scaffold generates (views/layouts/messages.html.erb), so the scaffolded views will use our standard layout. We already have a contact button in the navigation bar, but this is pointing to one of our initial static pages, and now we want it to point to the new contact form. So run the sample app (script/server), log in, go to the Page Admin, and edit the Contact page to set it to redirect (using the capability we added in the Lesson 17) to the new action in the messages controller. Since we’ve hijacked a scaffolded form that was meant to be part of the admin and are using it for a user-facing form, we need to tweak it a bit. Delete this line from the end of views/messages/new.html.erb, since we don’t want visitors to try to get to the list of all messages: <%= link_to 'Back', messages_path %> Redirecting to Home after Submission We don’t want to redirect to the message/show action, which is part of the admin interface and is what the scaffolding does by default. So we’ll change the redirect to go to the home page, and change the flash message to something more appropriate. Users who submit a message will see the thank-you message at the top of the home page. In the message controller’s show action: if @message.save flash[:notice] = 'Thanks for Your Message' format.html { redirect_to root_path } Later in this lesson, we’ll further modify this code to actually send the messages as an email, in addition to saving it to the database. Message validations We want to be sure that the message looks valid before processing it, so we add the following validations to the message model (models/message.rb): validates_presence_of :name, :subject, :body validates_format_of :email, :with => /^(\S+)@(\S+)\.(\S+)$/ The second validation ensures that the email looks like a valid email address. This messy regular expression is one of the simpler ones of many that could be used. To apply some styling to the error messages that are displayed if a validation fails, modify the stylesheet line in application.html.erb: <%= stylesheet_link_tag 'learningrails', 'scaffold' %> This includes the standard Rails scaffold.css file, which the rails script created as part of the initial creation of the application. Fetching the Page Object To keep the Contact Us tab highlighted while the contact form is displayed, and to set the page title, we need to fetch the page object and set the pagetitle instance variable. We did this already for the links_controller/list action in the Lesson 17; let’s extract that code, and put it in a method that we store in application.rb: def get_page_metadata @page = Page.find_by_name(params[:name]) @pagetitle = @page.title end Now we can use this same method in both links_controller/list and messages_controller/new. In the future, we’ll include this method in any action to which we’re redirecting via the CMS. Setting Up the Messages Admin For convenience, we’ll edit the admin page (using the CMS) to add a link to the message admin: "Message Admin":/messages We don’t want site visitors being able to view the list of contact messages, so we need to add authentication to the contacts controller. But we do need the “new” action in this controller to be accessible to visitors, as well as the “create” action that is invoked by the form when it is submitted. So add the following line to the start of controllers/messages_controller.rb: before_filter :login_required, :except => [:new, :create] Preparing to Send the Message Setting Up the Mailer Thanks to the scaffold generator and our CMS, we hardly had to write any code to create the contact form or the admin interface that allows us to read them. That was the easy part — now we want to generate an email to the web site manager with the contents of the message. Creating the Mailer The first step is to create a special Rails model called a mailer. As with other things in the Rails world, there’s a generator to make them. The command is: script/generate mailer contact_mailer message This creates a mailer model, called contact_mailer, and its associated view, which we’ve called message. You can enter multiple views here, for example if you had different kinds of contact forms and wanted to format each message differently. Configuring How Mail is Sent To actually send mail, we need to tell Rails how it supposed to access the mail system. You can configure it to use an SMTP server, or you can tell it to use sendmail, which is the more common approach if you’re on a Unix-type system; you’ll need a few details from the system administrator for the configuration for settings. In development mode, you’ll probably want to use an SMTP server to which you have access for testing. For example, here’s the configuration for sending email via SMTP over a Comcast connection: ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.smtp_settings = { :address => 'smtp.comcast.net', :domain => 'comcast.net' } We’ll add this code to config/environments/development.rb. You’d need to add similar code to production.rb for whatever SMTP server you’re using in your production environment. You can use :test for the method instead of :smtp, in which case all the messages are stored in an array, instead of being sent out. SMTP servers are picky about what mail they will accept, so if you’re not on a Comcast connection you’ll have to modify these settings. You can also specify additional parameters, including the port number, if it is not the default 25, as well as a user name and login if authentication is required. If you’re having trouble getting your mail to go out in development mode, you may want to change this line in your development environment: config.action_mailer.raise_delivery_errors = false And set it to true instead. If you don’t do that, delivery failures are silently ignored in development mode. Setting Up the Mailer Model Once you’ve run the generator, you’ll find a file contact_mailer.rb in your models folder. Open that file, and you’ll find one method, message. The method is in a class that inherits from ActionMailer::Base, rather than ActiveRecord::Base like all your other models, so it has a different set of characteristics and capabilities. We’re going to replace this method with the following: def message(message) subject message.subject body :message => message recipients CONTACT_RECIPIENT from message.email sent_on Time.now end Note that we’re not setting variables with names like subject; we’re invoking methods, which are available to all mailer methods, and passing parameters to those methods (earlier versions of rails used instance variables instead). The subject is simple enough; we’re just setting the parameter for the subject method to the subject attribute of the message, which we’ve passed into this method. For the body, we provide a hash that has the name of the instance variable we want to pass, and the value of that variable. We have only one variable here; you could have several. This passes the message object to an instance variable named @message that will be available in the view. Now we set the recipient to a constant, rather than a literal value, because we don’t want a specific email address, which may change in time, deep in our code. Add this line in your config/environments/development.rb file: CONTACT_RECIPIENT = 'yourname@yourdomain.com' Another benefit of this approach is that you can set a different address in your production.rb environment file. For example, in development, you’ll probably want contact messages to go to you, but in production, they typically go to an administrative or sales person. Finally, we set the “from” address and the time. That’s it for the mailer model. Now on to the view. Creating the Mailer View You’ll find an empty view in views/contact_mailer/message.erb. This is the view that will get invoked when we use the mailer model to request deliver of a message. The instance variables passed to this view are those we defined in the @body variable in the model. Now we use them just like in a regular view, but remember that we’re generating a plain-text email here, so there’s no HTML code required. Everything we need is in the @message variable, which holds the message object created from the form. Here’s a simple view: Email from your web site From: <%= @message.name %> Company: <%= @message.company %> Phone: <%= @message.phone %> Message: <%= @message.body %> Delivering the Mail With all this setup behind us, actually delivering the mail is easy. Open the file controllers/messages_controller.rb, and add this line immediately after “if @message.save” (we only want to deliver the mail if the save was successful, indicating that any validations passed): ContactMailer.deliver_message(@message) We’re invoking the message method in the ContactMailer class (defined in models/contact_mailer.rb). We pass to that method the message object from the form. There’s one strange thing here: the method we’re invoking is “deliver_message”, not “message”. Rails creates this method name for us, and we have to use it. Just one of those oddities to get used to. (This odd syntax exists to support another option: you can call the create_message method, which will produce the mail object, ready to be sent, but won’t actually send it.) You can now start the server and create a contact message, and it should be sent. Depending on what system you’re running on, and how your mail delivery is configured, it may not actually go out, but you can look at the Rails log (which should be in the console window in which you started the server), and it will show the email that was generated, even if it couldn’t contact a mail system to actually send it. Sending HTML and Multipart email You can easily send HTML or multipart email as well. If you simply name your view files appropriately, Rails will automatically produce multipart mail, so mail readers that accept HTML will get that, and others will get text. Rename message.erb to message.text.plain.erb, and make a new file in that same folder called message.text.html.erb. In that file, put an HTML version of the message, such as: <h1>Email from your web site</h1> <ul> <li>From: <%= @message.name %></li> <li>Company: <%= @message.company %></li> <li>Phone: <%= @message.phone %></li> </ul> <p>Message: <%= @message.body %></p> Now when the message is sent, it will be sent as a multipart message.

 17: Resources Page: Links, Categories, and HABTM | File Type: video/mpeg | Duration: 00:37:00

Goals In this lesson, we’re creating the database-driven Resources page, with links shown by category. Along the way, we look at join tables and HABTM associations. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 16. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the start of this lesson Learning Rails example app code as of the end of this lesson Adding the Links and Categories Models For each link, we want to have a title and a description, and of course we need the URL. Let’s use the scaffold generator to create the model and the admin page: script/generate scaffold link url:string title:string description:text The “text” field type can hold longer strings that the “string” type, which is why we used it for the description. We also need a model for the categories, so let’s run another scaffold command for that simple model, which requires only a title and a description for each category: script/generate scaffold category title:string description:text Creating the Join Table When you have an association where one model has a belongs_to declaration, you know that model must have a field to store the foreign key that is what creates the association. In the case of Categories and Links, however, a category can have many links, and a link can belong to many categories, so what we need here is a has_and_belongs_to_many association. In this type of association, neither of the associated tables stores any foreign keys; instead, a separate join table stores just the pairs of foreign keys that define each association (i.e., associate one link with one category). We need to explicitly create this join table. There is no model associated with this table; it is just a database table that is use automatically along with the two associated models. Let’s generate an empty migration file with the migration generator: script/generate migration LinkCategoryJoin Note that the name is entirely arbitrary; we just want something that reminds us of what this migration is for. Now we define the migration by writing the self.up method: def self.up create_table :categories_links, :id => false do |t| t.integer :category_id t.integer :link_id end end This creates a table with two columns, each of which is one of the foreign keys. This table doesn’t get an id column of its own, so we add the option :id => false to the create_table method call. There’s several Rails naming defaults that come into play here, and you need to know what they are to write this code correctly: Join tables are always named with the names of the two associated tables, in alphabetical order, separated by an underscore. That’s why the table is called categories_links, and not links_categories (which won’t work because of this default). The foreign key fields are named with the name of the table they are referencing, with _id appended. The foreign key is referencing a single element in that table, so it uses the singular name (e.g., category_id, not categories_id). To keep our migrations reversible, we’ll add the down method: def self.down drop_table :categories_links end Migrate! We’ve now created three new migrations, one for categories, one for links, and one for the join table. A single command runs them all: rake db:migrate Model Associations We’ve defined the foreign keys when creating our models, but we still need to specify the associations for the model classes. In models/link.rb, we need to add to the empty Link class: has_and_belongs_to_many :categories This statement creates the association “link has and belongs to many categories.” Its presence allows us to write elsewhere in our code link.categories to retrieve the list of categories that has been assigned to this link. The join table that represents these assignments is managed for us automatically by the Rails framework. While we’re modifying the Link class, let’s require that at least a title be entered; there’s not much use to having a record without one: validates_presence_of :title Now we need to make a corresponding set of additions to the Category model. We’ll add to that empty class: has_and_belongs_to_many :links validates_presence_of :title This HABTM declaration allows us to write elsewhere in our code category.links, to find all the links associated with a category. Finishing up the Admin Interface You can now browse to localhost:3000/links to see the link admin page, but it still needs a bit of work. The Rails scaffold generator generates an empty layout file for every scaffold, assuming for some reason that we probably want a unique layout for each one. We don’t — we want to use the standard application layout. So we need to delete the extra files that the scaffold dumped into views/layouts. To protect the admin pages from public users, add to links_controller and categories_controller: before_filter :login_required For ease of access, let’s add links to our new admin pages to the admin home page. Log into the site, click the Admin button, and then click the Page Admin link. Click the Edit link for the Admin page, and add the following to the links that make up the page body: "Category Admin":/categories "Link Admin":/links (You can also use the Edit link on the Admin page, which triggers the in-place editor we created a couple lessons ago.) Setting Categories Now you can use the newly-created Category Admin link on the admin home page to create some categories; the scaffolded interface does everything we need. Go ahead and make a few. You can also add a link, using Link Admin, but there’s no place on the scaffolded link admin pages to specify the category. That’s because when we created the scaffold, the association didn’t exist. To use the standard Rails scaffolding, you need to manually modify the scaffold-generated view files to display or modify fields that come from associations. To create a category selector control on the new link form, we use the following slightly messy bit of code: <p> <b>Category</b><br /> <%= f.collection_select :category_ids, Category.find(:all, :order => 'title'), :id, :title, {}, :multiple => true %> </p> The collection_select method creates an HTML form element that allows the user to choose from a list of items. The parameters passed are: :category_ids — the attribute this element is setting Category.find(:all, :order => 'title') — an array of objects that creates the list of choices :id — the value field of the objects in the list array :title — the name field of the objects in the list array {} — placeholder for an options hash that we’re not using :multiple => true — option to allow user to select multiple items With this code inserted in views/links/new.html.erb, you should now have a list of categories from which to choose (provided that you have added some categories to the database already). The same bit of code for the category selector needs to be added to the edit view as well. Even better, you could pull out the form guts into a partial, and invoke the same partial from both the new and edit view, as we did in a previous lesson. Now you can create some links and assign them to categories. The Resources Page For the Resources page, we want to show a list of all the links, sorted by category. The index action of the links controller is already use as part of the admin interface, so we need another action. We’ll add this list action to links_controller.rb: def list @categories = Category.find(:all, :order => 'title') end This action is simply providing the view with a list of categories. We also need to change the before_filter we set for login, so this action won’t require login. Change the line at the top of the controller to: before_filter :login_required, :except => [:list] Now, we need to create a new file, views/links/list.html.erb, to respond to this action; here’s the basic code: <h1>Resources</h1> <% for category in @categories %> <h2><%=h category.title %></h2> <p><%=h category.description %></p> <ul> <% for link in category.links %> <li><%= link_to link.title, link.url %></li> <% end %> </ul> <% end %> Finally, since we’ve added another method to a RESTful controller, we need to declare that method in the route. In config/routes.rb, there is already a set of standard routes declared: map.resources :links To add the route, we modify this as follows: map.resources :links, :collection => {:list => :get} This specifies that the additional route operates on the entire collection of objects (not on a specific object), that its name is :list, and it responds to HTTP GET requests. Our list now works if you access it at localhost:3000/links/list. One small issue: if there’s an empty category (a category with no links) the category heading is still displayed. To make that go away, add a conditional around the loop in the list view: <% for category in @categories %> <% unless category.links.empty? %> <h2><%=h category.title %></h2>   Redirecting Navigation Now we have our Resources page being created from the database, and admin pages to add categories and links. Our last task is to connect it up the the navigation buttons. So far, our Resources page is a text page in our CMS. We’d like the Resources button to go to our list of links (/links/list), but the CMS doesn’t currently give us a way to do that. We want to keep using the page model to control navigation buttons, but allow page content to come from another controller, rather than from the page viewer. There’s many different ways to tackle this problem. The one we’ve chosen here, as the simplest to implement, is to add attributes to the Page model so any page can be specified as a “redirect” page, which should not be rendered by the viewer but instead by another controller and action. Let’s create a migration to add these elements to the model: script/generate migration PageRedirect The up method adds three attributes: def self.up add_column :pages, :redirect, :boolean add_column :pages, :action_name, :string add_column :pages, :controller_name, :string end And what up giveth, down taketh away: def self.down remove_column :pages, :redirect remove_column :pages, :action_name remove_column :pages, :controller_name end Now make the changes to the database: rake db:migrate Updating the Page form And now we just need to add the new fields to views/pages/_form.html.erb: <p> <b>Redirect?</b><br /> <%= f.check_box :redirect %> </p> <p> <b>Action</b><br /> <%= f.text_field :action_name %> </p> <p> <b>Controller</b><br /> <%= f.text_field :controller_name %> </p> Acting Upon Redirect Pages Now that we have this redirect information in the page model, we need to use it! We replace the link_to that generates the nav buttons, in the application layout, to: <% if page.redirect? %> <%= link_to page.navlabel, :action => page.action_name, :controller => page.controller_name, :name => page.name %> <% else %> <%= link_to page.navlabel, view_page_path(page.name) %> <% end %> We pass the page name as a parameter so the action to which we’ve redirected can load the appropriate page object to get the page title and control the tab highlighting. So in the list action in the links controller, we add: @page = Page.find_by_name(params[:name]) @pagetitle = @page.title Now we need to edit the Resources page’s entry in the CMS to set it to redirect to the list action and the links controller, and voila! Our Resources button now takes us to the database-generated page.

 16: Clean-Up | File Type: video/mpeg | Duration: 00:17:00

Goals In this lesson we’re taking a break from adding features to do a little cleanup: Get the page title logic out of the application layout Add page titles for admin and login pages Make navigation buttons indicate which is the current page Add a ‘back to previous’ navigation link on subpages Add a little styling to subnav links Set the focus for the login field Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 15. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 15 Learning Rails example app code as of the end of Lesson 16 Fix up Page Title Handling Several lessons ago, as we were building the core of our content management system, we put a quick hack in the application layout (views/layouts/application.html.erb) that put this logic in the wrong place; we want to keep logic out of our views as much as practical. So let’s replace this line: <title><%= @pagetitle || (@page && @page.title) || 'Learning Rails Sample Application' %></title> with the much simpler: <title><%= @pagetitle %></title> Now we need to set this instance variable appropriately for each view. First, in the viewer_controller show method, insert the line (after @page is set): @pagetitle = @page.title That takes care of all pages that come from the content management system. Now we need to take care of the admin and login pages. Add to users_controller: before_filter :set_pagetitle def set_pagetitle @pagetitle = 'User Administration' end This before filter will run before every action, so the page title will be set (to the same thing) for all of them. In page controller, we already made a very similar addition in a previous lesson, but we called it set_metadata, so for consistency we’ll rename it to set_pagetitle Finally, the sessions controller needs some attention. Add to the (empty) new action in sessions_controller: @pagetitle = 'Please Log In' And after the else in the sessions_controller’s create method: @pagetitle = "Login was not successful" You could do more to customize page titles for the various admin pages, but this is enough for us. Highlight Current Nav Button Next problem: make the appropriate nav button highlight to indicate the current page. First, we need to identify which nav button to highlight by using some Ruby code to conditionally insert an ID. We’ll insert the following code in the application layout, in the <li> tag that begins each nav button: <li <%= "id = 'current'" if @page && @page == page %>> Now we need a CSS rule, in public/stylesheets/learningrails.css, to style the button for the current page: #navbar #current a { background-color: #000; } Now the navigation button for the current page is highlighted. Provide a Link Back to Parent Page When we added subpages in the previous lesson, we didn’t provide any indication when you’re on a subpage as to what the parent page is. There’s several things we might do about this; here’s two possibilities: We could use the subnav div area to show a “Return to About Us” link, for example, on subpages of About Us. To do so, replace the code at the top of views/viewer/show.html.erb with the following: <% if !@subpages.empty? %> <div id='subnav'> <ul> <% for page in @subpages %> <li><%= link_to page.navlabel, view_page_path(page.name) %></li> <% end %> </div> <% elsif @page.parent %> <div id='subnav'> <ul> <li><%= link_to "Return to #{@page.parent.navlabel}", view_page_path(@page.parent.name) %> </li> </ul> </div> <% end %> If there are subpages, then this code displays the subpage nav. If not, then if there is a parent page, it displays a link back to the parent page. (Note that you’d want to do something a little different if you had multiple levels of subpages.) Now the subpages of About Us show the link back. Add a Little Style The subnav lists are pretty ugly, so let’s style them a little to make them look like buttons. Add the following to public/styles/learningrails.css: #subnav { width: 160px; float: left; margin: 0 20px 50px -10px; } #subnav ul { list-style: none; font-weight: bold; font-size: 13px; margin: 0; padding: 0; } #subnav ul li { background-color: #999; border-bottom: 1px solid #333; border-top: 1px solid #333; } #subnav ul li a { padding: 5px 10px; display:block; color:white; text-decoration: none; } #subnav ul li a:hover { background-color: #333; color: #fff; } Retain Parent Button Highlight On Subpages Another way we could indicate the parent page would be to keep its navigation button highlighted. We can do this with a small change to the current page id logic in the application layout, adding a test for the parent of the current page: <li <%= "id = current" if @page && (@page == page or @page.parent == page) %>><%= link_to page.navlabel, view_page_path(page.name) %></li> Now when you’re viewing one of the subpages of the About Us page, the About Us button is highlighted. Note that we’ve let some logic creep back into our application controller here, but it is really presentational logic, so it doesn’t feel too bad. Set Form Field Focus One last bit of cleanup. On our login page, it’s annoying that you can’t just begin typing; neither of the form fields have focus until you click in one of them. We can fix that by adding a little bit of JavaScript to the bottom of views/sessions/new.html.erb: <%= javascript_tag "$('login').focus()" %> And now you can start typing as soon as the login page is displayed. Moving On We could go on for several more lessons tuning up our little content management system, but it’s time to move on. Next lesson, we’ll create the resources database and use that to drive the Resources page.

 15: Pages and Subpages | File Type: video/mpeg | Duration: 00:26:00

Goals In this lesson we’re adding a hierarchy to our pages. Instead of a single pool of pages with a navigation button for each, we want to have subpages as well, which don’t appear in the top navigation bar but are listed in second-level navigation on their parent page. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 14. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 14 Learning Rails example app code as of the end of Lesson 15 Model association To associate subpages with their parent pages, we could create a subpage model, and then we could write in the page.rb model: has_many :subpages And in the subpage.rb model: belongs_to :page But to do it this way creates a lot of duplication, since the subpage model would need to behave just like the page model. So we use what’s called a self-referential association: a page object has many page objects, and a page can have (belong to) a parent. This requires a more complex declaration in the page.rb model, as we’ll see, but once that’s done, the self-referential model works the same as it would if subpage was a separate model. Extending the Page table We need to add some fields to the Page model. As with any change to the database structure, we create new migration, with whatever name we’d like: script/generate migration AddSubpages These three lines in the “up” method create the new fields: def self.up add_column :pages, :parent_id, :integer add_column :pages, :navlabel, :string add_column :pages, :position, :integer end And the corresponding three lines in the “down” method allow us to set the database back to its prior condition, should we want to roll back the database: def self.down remove_column :pages, :parent_id remove_column :pages, :navlabel remove_column :pages, :position end Once we’ve created and saved the migration file, we apply the migration: rake db:migrate Defining the association In the Page class, we need to specify the relationship between parent pages and subpages. We have a field, parent_id, that we created in the previous migration to serve as the foreign key for the association. Since this is a self-referential association, the default naming schemes don’t apply, and we need to explicitly specify the class name and foreign key field name: has_many :subpages, :class_name => 'Page', :foreign_key => 'parent_id' The has_many declaration allows us to then write, typically in our controllers, page.subpages, to retrieve all the pages that have the current page as their parent. Since this is a self-referential association, the “belongs_to” side of the relationship also goes in the page model: belongs_to :parent, :class_name => 'Page', :foreign_key => 'parent_id' This declaration allows us to then write page.parent to find the parent page, if there is one. Writing Custom Finders Until now, we’ve mostly used standard find methods directly in our controllers. It’s a better design practice, however, to push logic for finding into the model. By writing a custom method to serve as a special kind of find, we can encapsulate more of how the page model works in that single file. Here’s a method to provide the complete set of navigation tabs: def self.find_main Page.find(:all, :conditions => ['parent_id IS NULL'], :order => 'position') end And here’s a variant that only shows tabs for site visitors (i.e., no pages that have their “admin” attribute set): def self.find_main_public Page.find(:all, :conditions => ["parent_id IS NULL and admin != ?", true], :order => 'position') end Creating main navigation tabs Now we need to update the method that tells the layout what tabs to display. We created this method in an earlier lesson and put it in controllers/application.rb, which is “mixed in” to every other controller. Let’s modify get_pages_for_tabs in application_controller to use the new finds: def get_pages_for_tabs if logged_in? @tabs = Page.find_main else @tabs = Page.find_main_public end end Using the navlabel text We have a new attribute in the page model for the navigation button text, so let’s change the line of code in views/layouts/application.rhtml.erb from: <li><%= link_to page.title, view_page_path(page.name) %></li> to use the new attribute: <li><%= link_to page.navlabel, view_page_path(page.name) %></li> Providing access to the new attributes We need to update the admin forms for the Page scaffold to let us set and modify the new attributes. Instead of changing both new and edit views (in views/pages), we create a form partial that is used for both the new and edit views. Copy the guts out of either the new or edit view: <p> <b>Name</b><br /> <%= f.text_field :name %> </p> <p> <b>Title</b><br /> <%= f.text_field :title %> </p> <p> <b>Body</b><br /> <%= f.text_area :body %> </p> <p> <b>Admin?</b><br /> <%= f.check_box :admin %> </p> Paste this into a new file in views/pages, called “_form.rhtml.erb”. The underscore identifies it as a partial. Now, in both the new and edit views, all this text can be replaced with: <%= render :partial => 'form', :locals => {:f => f} %> To create the HTML pop-up menu for choosing the parent page, we use the collection_select method: <p> <b>Parent Page</b><br /> <%= f.collection_select :parent_id, Page.find(:all), :id, :title, :include_blank => true %> </p> Add a field to specify the position: <p> <b>Position</b><br /> <%= f.text_field :position, :size => '3' %> </p> And a field for the nav label: <p> <b>Nav Label</b><br /> <%= f.text_field :navlabel %> </p> Creating subpages Now we can use the page admin interface to set the navlabel and position for each of the existing pages. Then let’s create some subpages. For our example we create two pages, Services and Products, that have About Us as their parent page. The main navbar should still show only the main pages, labeled and sorted according to our new navlabel and position attributes. Creating second-level navigation links To create the second-level menu, we need to find the subpages in the viewer controller’s show method, by adding the following between the two existing lines: @subpages = @page.subpages At the top of views/viewer/show, we’ll add a simple list of the any subpages: <% unless @subpages.empty? %> <div id='subnav'> <ul> <% for page in @subpages %> <li><%= link_to page.navlabel, view_page_path(page.name) %></li> <% end %> </ul> </div> <% end %> And some minimal styling for this div, to put in the stylesheet: #subnav { width: 200px; float: left; border-right: 1px solid black; margin-right: 20px; } The About Us page now shows links for its two subpages, and clicking on those links displays those pages. Bugfix: there’s been a spurious <div> tag after the navigation links in the application layout, which we’ve deleted in the code for this lesson. Wrapping up We now have a usable two-level page structure. Before putting this into real use, we’d want better styling for the level-two navigation links, and some indication of what the parent page is when we’re on a subpage. In our next lesson, we’ll take are of a few of these lingering details, preparing to move on to the contact form and resources page in later lessons.

 14: Using Textile Markup, plus In-Place Editing with Ajax | File Type: video/mpeg | Duration: 00:40:00

Goals In this lesson we’re going to add two features to our content-management system: Textile markup and in-place editing. Both are, in principle, very simple to implement, thanks to a couple of Rails gems and plugins, as well as the Prototype and Scriptaculous JavaScript libraries. The reality turns out to be just as simple as we’d hope in the first example, and rather more complex in the second. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the link on the left. Setup We begin with the code with which we ended Lesson 12. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 13 Learning Rails example app code as of the end of Lesson 14 Using Textile markup We don’t want our administrative users to have to enter HTML code, so we’ll use a simple markup language called Textile. There’s two pieces of Ruby code we use to implement this. The first is the gem RedCloth, which you already have if you’re on Leopard. This is the code that translates Textile into HTML. The second is a Rails plugin called “acts_as_textiled”, which makes it trivially easy to add Textile markup to our content blocks. Run “gem list” to see if you already have RedCloth. If not: sudo gem install RedCloth To add the plugin, open a terminal window at the root of your application and enter the following command: script/plugin install svn://errtheblog.com/svn/plugins/acts_as_textiled Add to the Page model: acts_as_textiled :body That’s it. This plugin is smart enough to automatically display the Textile markup source when you’re displaying the body in a form field, as we are in the admin pages, but to render it into HTML anywhere else you use the body. You’ll need to stop and restart the server before the plugin will function. Now you can edit any of the pages in the content management system, such as the admin page, to convert the markup to Textile. Note that the page list view shows the HTML created by RedCloth — that’s because the acts_as_textiled plugin automatically renders the Textile content into HTML anyplace except in a form field. In-place editing In-place editing is a common feature of modern web applications, and there’s good support for it built in to Prototype. There’s also a Rails helper that wraps the Prototype method so we don’t even have to touch the JavaScript code. In Rails 1.2.x, the in-place editor helper was part of the framework. In Rails 2.0, however, it was split out as a plugin, in theory so it could be separately maintained. Unfortunately, it has not been maintained (as of this writing in early May 2008), so not only do we need to install the plug-in, we’re going to have to seek out and install a couple of patches before we have everything working. Installing the plugin Install the in_place_editing plugin by entering the following in a console at the root of your application: script/plugin install http://svn.rubyonrails.org/rails/plugins/in_place_editing Modifying our code to use the plugin The readme file provides basic installation instructions. We need to make two small changes to our code. In the file views/viewer/show.html.erb, replace the one line of text that is now in that file with the following: <%= in_place_editor_field :page, 'body' %> Then add this line to the top of the class in controllers/viewer_controller.rb: in_place_edit_for :page, :body And finally, we need to tell our application to load the JavaScript libraries. Add to the head section in views/layouts/application.html.erb: <%= javascript_include_tag :defaults %> Now restart the server, since we’ve installed a plugin, and everything should work as before. But now, when you click on any page text, a little edit box comes up! Try editing the text and click OK. Unfortunately, the save fails. To get a hint of what’s happening, view the page using Firefox with Firebug installed, and take a look at the console when you click the OK button. Scroll through the response and you’ll see that there’s an issue with the authenticity token. The CSRF protection bug The authenticity token is a feature added to Rails 2.0 to improve protection against cross-site request forgery (CSRF) attempts. Unfortunately, the team didn’t update the in-place editor plugin correspondingly, so this security feature trips it up. Try a google search on “in place editing CSRF”. The first result is a ticket in the Rails Trac titled “in_place_editing plugin does not work with CSRF protection”! Look at the patch suggested; focus on the forgery protection, grab the four lines of code and paste them into vendor/plugins/in_place_editing/lib/in_place_macros_helper.rb (see the screencast for details). After adding these lines restart the server, reload the web page, click on some text to edit it, and click OK to save. Now it works! A minor correction to the screencast discussion of the JavaScript code: in the audio, we referred to Ajax.InPlaceEditor as a Scriptaculous method. In fact, the Ajax object is part of the Prototype library, and the InPlaceEditor method is added by Scriptaculous. Providing a larger text entry area Next we want to provide a larger text entry field. Add the rows and columns options to the helper invocation: <%= in_place_editor_field :page, 'body', {}, {:rows => 20, :cols => 80} %> Refresh the browser, click on the text, and you should now see a decent size text area. Displaying Textile source instead of HTML Next problem: It is displaying the HTML markup, not the Textile source. By default, the in_place_editor_field helper pulls the text to be edited from the page itself, not from the server, so naturally it sees the rendered HTML. To fix this, we need to set an option to tell it to fetch the source from the server, and then we need to create the controller code to respond to that request. The option we need is called load_text_url. With this added to the helper invocation, we now have the following code in the view file: <%= in_place_editor_field :page, 'body', {}, {:rows => 20, :cols => 80, :load_text_url => {:controller => 'viewer', :action => 'get_unformatted_text', :id => @page.id}} %> This tells the Scriptaculous method to make an XMLHttp request (the workhorse of Ajax applications) to retrieve the unformatted text. Now we need to add the get_unformatted_text action to viewer_controller.rb: def get_unformatted_text @page = Page.find(params[:id]) render :text => @page.body(:source) end Take a look at the acts_as_textile readme to find the (:source) option that enables us to access the markup source, rather than the rendered HTML. Reload the page, and it now displays Textile, just as we wanted. Do we want visitors to be able to edit the site? One small problem — unless our goal is to provide a wiki, we probably don’t want any visitor to be able to edit our site’s contents. So we need to use the in-place editor only if someone is logged in. Simple enough, just change the view code to: <% if logged_in? %> <%= in_place_editor_field :page, 'body', {}, {:rows => 20, :cols => 80, :load_text_url => {:controller => 'viewer', :action => 'get_unformatted_text', :id => @page.id}} %> <% else %> <%= @page.body %> <% end %> Refresh the browser, and now in-place edit is available only to logged-in users. Adding an external control One more problem: in admin mode, we can’t access links on the page, because clicking anywhere in the page text takes us into edit mode. So our admin dashboard is now useless. This would also be a problem if any of the regular pages had links and we wanted to be be able to exercise them in admin mode. Looking back at the in-place editor helper file, we see an option for externalControl, so let’s try that. Add one more option to our in_place_editor_field invocation: <%= in_place_editor_field :page, 'body', {}, {:rows => 20, :cols => 80, :external_control => 'edit', :load_text_url => {:controller => 'viewer', :action => 'get_unformatted_text', :id => @page.id} } %> And create an edit link at the top of the page to trigger this: <a href='#' id='edit'>Edit This Page</a> Refresh the page, and give the edit link a try. It works! Unfortunately, clicking on the text also triggers the edit control, so we need to somehow prevent that. Making only the external control activate the edit mode What we need is a way to have only the external control activate the in-place editor. Searching the web, we find confusing and contradictory results. But in the excellent book Prototype and Scriptaculous, we read up on the in-place editor options, and find a reference to an option externalControlOnly. Alas, the Rails helper doesn’t support this, so it’s back to patching the code. In the helper, we find the line: js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control] So let’s try duplicating this line in the helper, changing externalControl to externalControlOnly in the duplicate line, and then we change our view code to set this option to true: <%= in_place_editor_field :page, 'body', {}, {:rows => 20, :cols => 80, :external_control => 'edit', :external_control_only => true, :load_text_url => {:controller => 'viewer', :action => 'get_unformatted_text', :id => @page.id} } %> And presto, now it behaves as we want! Life in the open-source world is sometimes not as clean as we’d like it to be. Someone will no doubt update the in-place editor plugin soon, which will cut out more than half the effort of this exercise. Coming Up In our next lesson, we’re going to improve the navigation model for our content management system generated pages, so we can have sub-pages as well as main pages, and so the navigation button text can be different from the page title.

 13: Admin Pages | File Type: video/mpeg | Duration: 00:21:00

Goals In this lesson, we implement the actual administrative dashboard using improvements we make to the mini-CMS we are building. We finish up with an improvement to our navigation code so we can build the tabbed interface more dynamically. Setup We begin with the code with which we ended Lesson 12. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 12 Learning Rails example app code as of the end of Lesson 13 Admin Pages Of course, it is tiresome to keep typing the URLs manually to get to our administrative pages. Let’s create an admin dashboard page that connects us to these sub-pages. Let’s also make the admin page easy to reach when we are appropriately logged in. Make the Admin page attribute If we are going to use our baby-CMS to implement this page, what is missing? We need to know when a page is an admin type page so we can protect it properly. Make a migration to add an admin attribute to pages, and then set some default values in the migration to keep things tidy. class AddAdminPageAttribute < ActiveRecord::Migration def self.up add_column :pages, :admin, :boolean @pages = Page.find(:all) @pages.each do |page| page.update_attribute(:admin, false) end end def self.down remove_column :pages, :admin end end Now, we need to update the Page Admin html so we can see/edit the new admin attribute: First, index.html.erb gets a couple of new table entries: <h1>Listing pages</h1> <table> <tr> <th>Name</th> <th>Title</th> <th>Body</th> <th>Admin?</th> </tr> <% for page in @pages %> <tr> <td><%=h page.name %></td> <td><%=h page.title %></td> <td><%=h page.body %></td> <td><%= page.admin? ? "TRUE" : "FALSE" %></td> <td><%= link_to 'Show', page %></td> <td><%= link_to 'Edit', edit_page_path(page) %></td> <td><%= link_to 'Destroy', page, :confirm => 'Are you sure?', :method => :delete %></td> </tr> <% end %> </table> <br /> <%= link_to 'New page', new_page_path %> Then add a snippet to show.html.erb to see the Admin value: <p> <b>Admin?</b><br /> <%= @page.admin? ? "TRUE" : "FALSE" %> </p> Update both edit.html.erb and new.html.erb with a snippet to use a check box for the state of the admin attribute (put this in before the submit): <p> <b>Admin?</b><br /> <%= f.check_box :admin %> </p> We should be all set. Check it out and create a page with the admin bit set. Can you see it when you are logged in? What about when you aren’t? Oops! Update the viewer controller to handle special pages Of course, we don’t filter in anyway for admin pages, so anyone can see a page marked with the Admin bit. Let’s fix that by protecting admin viewable pages by updating the viewer controller: def show @page = Page.find_by_name(params[:name]) login_required if @page.admin? end We are leveraging the login_required function provided to us by the restful_authentication plugin. If the requested page has the admin value set to true, we’ll check to see if the user is logged in with login_required. Add an admin dashboard page in the DB Finally, we can create a page using the mini-CMS’ Page Admin. Make one now and we’ll call it “admin”. Make the page contents as follows: <a href="/pages">"Page Admin"</a> <br> <a href="/users">"User Admin"</a> Test it. Check it logged in and logged out. It works! Make layout dynamic It is getting really tiresome to keep updating our simple navigator, since we need to access the admin pages regularly, let’s take this opportunity to make a new tab appear for each page automatically. Update the application layout to support rendering tabs dynamically. Here is the new navbar div: <div id='navbar'> <ul> <% @tabs.each do |page| -%> <li><%= link_to page.title, view_page_path(page.name) %></li> <% end -%> <li><% if logged_in? %> <%= link_to "Log Out", logout_path %> <% else %> <%= link_to "Log In", login_path %> <% end %> </li> </ul> <div> We need to add a before_filter to application.rb to load up pages into that @tabs variable: before_filter :get_pages_for_tabs def get_pages_for_tabs if logged_in? @tabs = Page.find(:all) else @tabs = Page.find(:all, :conditions => ["admin != ?", true]) end end This won’t deal well with lots of pages, so in the future we’ll improve this solution with the notion of page groups, sub-navigation, and other tricks. Wrapping up Try out the new navigation at localhost:3000. You may want to tweak the titles of the pages to make the tabs look better. We are reusing the same data as is used for the page titles in the title bar of the browser. A better solution over time may be to add a separate attribute so we can keep SEO friendly titles. We finally have support for “administrative” pages in our simple CMS, so we can create the dashboard using our own technology! Coming up Next time, we’ll add the ability to use Textile markup in our CMS pages (via a plugin) and start playing with AJAX to make the UI a little easier to use.

 12: User Management | File Type: video/mpeg | Duration: 00:33:00

Goals In this lesson, we work towards implementing an administrative dashboard by expanding the user controller generated by restful_authentication to give us full abilities to manage user data. Setup We begin with the code with which we ended Lesson 11. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 11 Learning Rails example app code as of the end of Lesson 12 Clean up session create with a notice if user incorrectly enters credentials Before diving in to the core work in this lesson, let’s continue to do some small changes to improve the experience our application provides. One pain point occurs when you try to log in and type the wrong credentials. Let’s put a notice in the create failure path, before re-rendering the edit dialog flash[:notice] = "Try entering your credentials again" Create a default admin user (migration) Let’s use a migration called a “data migration”. It doesn’t change the structure of the database. Instead it loads data, in this case, a default user account. script/generate migration AddDefaultUser self.up if !User.find_by_login('admin') User.create(:login => 'admin', :email => 'admin@sample.com', :password => 'changeme', :password_confirmation => 'changeme') end end We aren’t creating a down migration in this case. We don’t know if we were the ones who created this account for sure, so we won’t delete it when this migration is run in reverse. Run the migration in the terminal: rake db:migrate Add a user list page The “restful_authentication” plugin’s generate creates a RESTful controller, but it only creates the new and create actions. We need to fill the rest out and can use the Pages controller as a model. Add an index action to users controller. def index @users = User.find(:all) end Create users/index view in the users directory (index.html.erb). Also prepare by adding links to show, edit, and delete. Put a link at the bottom to create new users. <h1>Listing Users</h1> <table> <tr> <th>Login</th> <th>Email</th> </tr> <% for user in @users %> <tr> <td><%=h user.login %></td> <td><%=h user.email %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Delete', user, :confirm => "Are you sure you want to delete '#{user.login}'?", :method => :delete %></td> </tr> <% end %> </table> <br /> <%= link_to 'New User', new_user_path %> Take a look…seems to work, but we need to fill out the rest of the actions and the appropriate views. Add show user page Add a show action to the users controller. def show @user = User.find(params[:id]) end Add the show view (show.html.erb). <p> <b>Login:</b> <%=h @user.login %> </p> <p> <b>Email:</b> <%=h @user.email %> </p> <p> <b>Password:</b> [secret] </p> <%= link_to 'Edit', edit_user_path(@user) %> | <%= link_to 'Back', users_path %> Add a user delete function Add destroy action to users controller. def destroy @user = User.find(params[:id]) @user.destroy redirect_to(users_url) end Note the link is already in the index view and uses a javascript alert dialog to confirm the deletion with the user. Add a edit/Update user functions Add edit and update actions to the users controller. New/create and edit/update always work in pairs in the RESTful implementation in Rails 2. def edit @user = User.find(params[:id]) end def update @user = User.find(params[:id]) if @user.update_attributes(params[:user]) flash[:notice] = 'User was successfully updated.' redirect_to(user_path(@user)) else render :action => 'edit' end end Create a corresponding view for edit. It is nearly identical to the new view, but we changed the form_for line to take advantage of Rails behavior. By specifying the object on the form_for line, Rails automatically generates the proper URL to our update action and submits the form using the “PUT” HTTP method. <%= error_messages_for :user %> <% form_for @user do |f| -%> <p><label for="login">Login</label><br/> <%= f.text_field :login %></p> <p><label for="email">Email</label><br/> <%= f.text_field :email %></p> <p><label for="password">Password</label><br/> <%= f.password_field :password %></p> <p><label for="password_confirmation">Confirm Password</label><br/> <%= f.password_field :password_confirmation %></p> <p><%= submit_tag 'Update' %></p> <% end %> Now test all of the pages together. They work! Wrapping up Of course, it is tiresome to keep typing the URLs manually to get to these pages. You can navigate directly to the users controller at localhost:3000/users, but it would be a lot easier to have a dashboard that collects all of these links in one easy to access place. Even better, the dashboard should be available from our navigation tags when appropriately logged in. Coming up In our next lesson, we’ll add support for “administrative” pages in our simple CMS, so we can create the dashboard using our own technology. We’ll also tweak the tab navigation so it automatically discovers and displays links without us needing to edit code.

 11: Adding User Authentication | File Type: video/mpeg | Duration: 00:23:00

Goals In this lesson, we add user authentication so only logged-in users can access the page controller and modify the contents of the site. Setup We begin with the code with which we ended Lesson 10. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 10 Learning Rails example app code as of the end of Lesson 11 Page title cleanup First, we need to take care of a little problem we created for ourselves with the way we handled the page title metadata in the previous lesson. We’re going to add a couple more admin controllers in this lesson, and with the approach used in the previous lesson, we’d need to specifically set the @pagetitle instance variable in each of those controllers. For now, we’re going to replace the existing setting of the title tag in the application layout with the following: <title><%= @pagetitle || (@page && @page.title) || "Learning Rails Sample Application" %></title> This line sets the pagetitle in one of three ways: If there is an instance variable @pagetitle, use that If not, then use @page.title, if there is a @page object If there’s neither a @pagetitle nor a @page object, use the fixed string This is a little messy, and in a later lesson we’ll fix it in a more elegant way. Install plugin The plugin we’re going to use is called restful_authentication. You can find plugins by searching on Google or on various Rails plugin directory sites. One good site is AgileWebDevelopment. From this site, you can get the URL for the repository where the plugin code is located. To install restful_authentication, enter the following line in a terminal at the root of your application: script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/ You can view the readme in vendor/plugins/restful_authentication to learn more about it. Set up the plugin This plugin provides a generator of its own to help us set things up, so let’s run it: script/generate authenticated user sessions In this command, “authenticated” is the name of the generator; “user” is the name of the model that will store user names and passwords; and “sessions” is the name of the controller that will manage user sessions. This generator creates a migration for us, but we need to run the migration: rake db:migrate Finally, to make the methods provided by this plugin available in your controllers, you need to add the following line to the file controllers/application.rb: include AuthenticatedSystem The application controller is “mixed in” to all other controllers, so any code you put here is available to all controllers. So by putting the “include” statement for the authenticated system here, you’re effectively adding that code to every controller. Creating a user Before we can have log-in, we need to have users, so let’s add some user management. The authenticated generator we ran in the previous section gave us a users controller and a view for creating users, so browse to //localhost:3000/users/new and create a login for yourself. The generator also created a sessions controller, which is what handles the actual log in and log out operations. Logging in is creating a new session, and logging out is destroying a session. It’s convenient to have shortcuts we can use to refer to these, so let’s add some named routes. Add the following lines to the top of the config/routes.rb file: map.logout '/logout', :controller => 'sessions', :action => 'destroy' map.login '/login', :controller => 'sessions', :action => 'new' This gives us two things. The text ‘/logout’ and ‘/login’ makes those simple URLs work. And the words after the “map.” provide shortcuts we can refer to anywhere in our code when we want to generate that URL, using either login_path, for example, to generate a relative URL, or login_url, to generate a full, absolute URL. Let’s add a log-in button to the navigation bar. Open views/layouts/application.html.erb, and add this code to the end of the list of links that creates the nav bar: <li> <% if logged_in? %> <%= link_to "Log Out", logout_path %> <% else %> <%= link_to "Log In", login_path %> <% end %> </li> The restful_authentication plugin provides us with a “logged_in?” method that we can use to determine if any user is logged in. If someone is logged in, then we display a log out link; if not, we display a log in link. Using authentication to protect the admin pages Now we have our entire authentication system in place, but we haven’t actually used it to require login for any pages. We need to invoke the authentication feature for the pages for which we want to require log-in. Open the file controllers/pages_controller.rb, and add the following line: before_filter :login_required “login_required” is another method provided for us by the restful_authentication plugin. The before_filter causes this method to be executed before any action in the controller. Now do the same for users_controller. Take care of page titles Just as in the previous lesson, we need to set the @pagetitle instance variable in the user and session controllers, since these pages aren’t generated from page objects. Wrapping up Try accessing localhost:3000/pages, and you should be presented with a login screen. Log in with the credentials created in the previous step, and you should see the Log In button in the nav bar change to Log Out. Try creating a new user by accessing localhost:3000/users/new. You shouldn’t be able to create a new user without being logged in. (If you didn’t create a user before adding the login requirement to the users controller, you can comment that line out temporarily.) Coming up In our next lesson, we’ll make a variety of small enhancements to add some polish and additional capabilities to our content management system.

 10: Putting the Page Contents into the Database | File Type: video/mpeg | Duration: 00:35:00

In this lesson, we create our first database table and Rails model.   In the previous lesson, we created static pages, with all the contents in the view files. The problem with this approach is that to change any text, you must modify the code. A better approach is to put the page contents into the database, so we can then provide an administrative interface that allows non-technical users to modify the pages. In this lesson, we do just that, creating the core of a simple content management system (CMS). Setup We begin with the code with which we ended Lesson 9. These zip files contain the beginning and ending states of the code: Learning Rails example app code as of the end of Lesson 9 Learning Rails example app code as of the end of Lesson 10 See Lesson 8 for pointers on setting up your development environment. In Lesson 8, we explained how to set up the MySQL database. In this lesson, we use SQLite, which is a little easier to get going (if you’re using Leopard, you don’t have to do anything to use SQLite). If you want to use MySQL instead, you just need to change the settings in database.yml. In particular, in the development section, replace the code in the sample application with something like this: adapter: mysql database: learning_rails username: root (or other user name) password: secretword (whatever your password is; leave blank if none) host: localhost You’ll also need to create the database, using the mysql command-line interface or one of the GUI interfaces (we’re partial to Navicat). With sqlite, this is done for you automatically. Lesson Outline Here’s the coding steps we take in this lesson. Watch the screencast for details and explanations (see link at the top of the left column). 1. Delete the four static views in views/pages 2. Create a scaffold for the new Page model with this command: script/generate scaffold page name:string title:string body:text 3. Run the migration: rake db:migrate 4. Start the server: script/server 5. Delete the automatically generated layout file views/layouts/pages.html.erb 6. Create the four pages (home, about, contact, resources) through the admin interface: Browse to http://localhost:3000/pages/new 7. Create a controller and view for the public view of the page: script/generate controller viewer show 8. In controllers/viewer_controller.rb, add this line to the show method: @page = Page.find_by_name(params[:name]) 9. Replace the boilerplate text in views/viewer/show.html.erb with this line: <%= @page.body %> 10. You can now access the home page using the explicit URL: http://localhost:3000/viewer/show?name=home 11. Create a route to clean this up. Add this line to config/routes.rb, after the map.resources line: map.view_page ':name', :controller => 'viewer', :action => 'show' 12. Update the route for the root (home) page (we created this line in Lesson 9): map.root :controller => 'viewer', :action => 'show', :name => 'home' 13. Fix the navigation links in views/layouts/application.html.erb: <li><%= link_to 'Home', view_page_path('home') %></li> <li><%= link_to 'Resources', view_page_path('resources') %></li> <li><%= link_to 'About Us', view_page_path('about') %></li> <li><%= link_to 'Contact Us', view_page_path('contact') %></li> 14. Change the title tag line so that the title is pulled from the database: <title><%= @page.title %></title> This will work for our normal pages, but it won’t work for the admin pages, since there isn’t always a valid @page object for those pages. So we need another way to set the page title for those pages. We change the layout to this: <title><%= @pagetitle || @page.title %></title> The || is the Ruby OR operator. If the item before the OR is not false, then the second item is not evaluated, so this expression essential says “use @pagetitle, unless it is nil; if it is nil, then use @page.title”. This enables us to set the pagetitle with an explicit instance variable in the admin controllers, and still use the @page object in the normal page controller. Now we need to set @pagetitle in the pages_controller. We want to set it to “Page Administration,” regardless of which action is executed. Rather than placing this code in every action, we can use a before filter, which runs the filter action before any other controller action. The code, which goes at the top of pages_controller.rb, is as follows: before_filter :loadmetadata def loadmetadata @pagetitle = "Page Administration" end Common Problems Note that the home page route that we set requires that you create a page named home in the database. If you haven’t created a page with this name, you’ll get an error when browsing to the base URL. (To create a page in the database, browse to localhost:3000/pages/new) Also, the navigation buttons assume there are pages with the names of about, resources, and contact, so if you haven’t created pages with those exact names in the database, you’ll get an error when you click on the corresponding button. Onward! We’re back to where we started! The site now works just like the one we created in Lesson 9, but now all the page contents are in the database, and we have a simple administrative interface to edit this text. There’s one big problem with this simple implementation: anyone who can guess the URLs for accessing our admin interface can modify the site! In the next lesson, we’ll add a user log-in system to provide authentication for accessing the admin interface. Resources For more on the SQLite database, see our list of SQLite 3 tutorials, software, and FAQs. Another Rails 2.0 scaffolding tutorial. Peepcode has a two-part introduction to Rails, titled Rails From Scratch. Ready to dive deeper into Rails? See our list of Ruby on Rails books

 9: The Simplest Possible Rails Application | File Type: video/mpeg | Duration: 00:27:00

Welcome to the first screencast in the Learning Rails free online course in Ruby on Rails. Note: we created this series in 2008, and the Ruby on Rails world, along with the software that supports it (such as various gems) has evolved quite a bit since then. Check the comments for suggestions on how to overcome various incompatibilities. We hope to have an updated version of the series by the end of 2010. If you want to follow along on your own system, you’ll need to have a basic Ruby on Rails development environment set up. Check Lesson 8 for details on how to do that. We’d love to hear any feedback you may have on this screencast. Please add your comment at the bottom of the page. In this lesson, we build a very simple Ruby on Rails application, with just a few static pages, so we can walk through the files that make up a Rails application and show how the view system works. The simple approach we’ve taken here isn’t something we would recommend for a production Rails application; it’s designed to cut away as much complexity as possible so we can focus on the essentials. In the next lesson, we’ll start building a more dynamic site. There’s not a lot of code in this first lesson, and we’ll be replacing it in the next lesson, but you can download the full application if you’d like. P.S. We’re still refining our production workflow, and the video in lesson is not quite up to our production quality goal. You’ll see improvements in future lessons.

Comments

Login or signup comment.