Creating Radiant Extensions
One of the most exciting aspects of Radiant 0.6 is the support that has been added for extensions. Since Radiant is a “no-fluff” content management system there are a lot of features supported by other systems that will never make it into Radiant. I’ve tried to keep things clean and simple so that Radiant is easy to learn and easy to support. The trouble is, my opinion of what features constitute “fluff” and what features are absolutely necessary is probably different than your own. Extensions give you the opportunity to change this.
Using extensions you can customize nearly every aspect of Radiant. And because Radiant is made with Ruby on Rails, developing an extension is almost as easy as developing a regular Ruby on Rails application.
In this tutorial I want to help you get started with your first extension. We will cover:
- Using extension generators
- Creating a custom model and controller
- Running extension migrations
- Creating custom tags
I will assume that you already have the latest Radiant gem installed on your local computer and that you have a basic understanding of Ruby on Rails. If you have never used Ruby on Rails before please run through the Rolling with Rails tutorials (Part I, II, & III) before you begin.
Creating a New Project
Let’s create a test project. Open up a command prompt and “cd” to the appropriate directory, then execute the `radiant` command to create a new project:
% radiant -d sqlite3 path/to/new/project
As you can see I’ve chosen to use SQLite 3 as my database engine, but you are welcome to choose MySQL or PostgreSQL instead.
The `radiant` command will create a skeleton for our new project and then output the following instructions:
== Installation and Setup
Once you have extracted the files into the directory where you would like to
install Radiant:
1. Create the MySQL/PostgreSQL/SQLite databases for your Web site. You only
need to create the "production" database, but you may also want to create
the "development" and "test" databases if you are developing extensions
or running tests.
2. Edit config/database.yml to taste.
3. Run the database bootstrap rake task:
% rake production db:bootstrap
(If you would like bootstrap your development database run `rake
development db:bootstrap`.)
4. Start it like a normal Rails application. To test execute:
% script/server -e production
And open your Web browser on port 3000 (http://localhost:3000). The
administrative interface is available at /admin/. By default the bootstrap
rake task creates a user called "admin" with a password of "radiant".
When using Radiant on a production system you may also need to set permissions
on the public and cache directories so that your Web server can access those
directories with the user that it runs under.
Once you've installed Radiant on your own Web site, be sure to add your name
and Web site to the list of radiant users:
http://dev.radiantcms.org/radiant/wiki/RadiantUsers
If you’ve chosen to use SQLite 3 as your database engine for this tutorial you can ignore steps 1 and 2. Otherwise create all three databases for the development, production, and test environments and edit “config/database.yml” to taste. Then run the bootstrap rake task for the production and development environments:
% rake db:bootstrap
% rake production db:bootstrap
Once you have bootstrapped both databases, start up the test server in production mode from the command line and verify that the site is running correctly before continuing:
% script/server -e production
Now go to “http://localhost:3000/admin/” and login to verify that everything is working correctly.
Once your are logged in click the “Extensions” link in the upper right corner to see the extensions that are currently installed:
Radiant ships with two pre-installed extensions at the moment. The “Markdown” and “Textile” extensions. On the extensions screen you can enable or disable extensions and view related information.
Now go back to the console and press Ctrl+C to stop the test server.
Generating an Extension
Let’s create our first extension. The extension we will be creating is one that will make it easy for us to manage a list of links on our Web site. To create a new extension you should use the extension generator. The format for the command is:
script/generate extension ExtensionName
In our case we will call our extension LinkRoll. At the command prompt type:
% script/generate extension LinkRoll
You should see the following output:
create vendor/extensions/link_roll/app/controllers
create vendor/extensions/link_roll/app/helpers
create vendor/extensions/link_roll/app/models
create vendor/extensions/link_roll/app/views
create vendor/extensions/link_roll/db/migrate
create vendor/extensions/link_roll/lib/tasks
create vendor/extensions/link_roll/test/fixtures
create vendor/extensions/link_roll/test/functional
create vendor/extensions/link_roll/test/unit
create vendor/extensions/link_roll/README
create vendor/extensions/link_roll/Rakefile
create vendor/extensions/link_roll/link_roll_extension.rb
create vendor/extensions/link_roll/lib/tasks/link_roll_extension_tasks.rake
create vendor/extensions/link_roll/test/test_helper.rb
create vendor/extensions/link_roll/test/functional/link_roll_extension_test.rb
As you can see, the extension generator has created a skeleton extension for us in the vendor/extensions/link_roll folder. The extension we will create will be entirely contained in the link_roll folder. To use it in another project all we need to do is copy the link_roll folder into that project’s vendor/extensions folder.
Open up the “link_roll_extension.rb” file. It should look something like this:
class LinkRollExtension < Radiant::Extension
version "1.0"
description "Describe your extension here"
url "http://yourwebsite.com/link_roll"
# define_routes do |map|
# map.connect 'admin/link_roll/:action', :controller => 'admin/link_roll'
# end
def activate
# admin.tabs.add "Link Roll", "/admin/link_roll", :after => "Layouts", :visibility => [:all]
end
def deactivate
# admin.tabs.remove "Link Roll"
end
end
Let’s edit the attributes of the LinkRollExtension. The attributes are listed in the first part of the class definition. First, change the “description” attribute to:
"Allows you to add a link roll to your Web site."
And change the “url” attribute to:
"http://dev.radiantcms.org/radiant/browser/trunk/extensions/link_roll/"
We will deal with the other two attributes in a moment. For now, let’s start the server up again:
% script/server -e production
Again, open your web browser up to “http://localhost:3000/admin/” and login. Click the “Extensions” link in the upper right corner. In the list of extensions you should now see the “Link Roll” extension:
Note that the description and the website correspond to the attributes that you just edited.
Generating a Model
Let’s go back to the command prompt for a moment and generate our first model. Press Ctrl+C again to stop the test server. Type:
script/generate extension_model LinkRoll Link title:string url:string description:text
This should output:
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/link.rb
create test/unit/link_test.rb
create test/fixtures/links.yml
exists db/migrate
create db/migrate/001_create_links.rb
As you can see the extension_model generator takes several arguments. “LinkRoll” is the name of the extension that we want to add a model to. “Link” is the name of our new model. And the rest of the parameters are attribute:type formatted attributes of the new model.
If you look at the output of the command, you will notice that it generated:
- the file for the Link model
- the unit test
- the links fixture
- the migration
Open up the migration file (“vendor/link_roll/db/migrate/001_create_links.rb”). It should look like this:
class CreateLinks < ActiveRecord::Migration
def self.up
create_table :links do |t|
t.column :title, :string
t.column :url, :string
t.column :description, :text
end
end
def self.down
drop_table :links
end
end
You can see that the migration is already setup to create a “links” table for the associated Links model object. Also note that the attributes on the Links model are configured to reflect the attributes that were originally passed into the extension_model generator command.
Let’s run the migrations for this model (be sure to run them on both the development and production databases):
% rake db:migrate:extensions
% rake production db:migrate:extensions
(Note: You can safely ignore this for now, but: Later, if you want to migrate your database to a specific version of your extension’s schema (for example an earlier version to roll-back changes), you must use the migration task that was generated specifically for your extension, for example:
% rake production radiant:extensions:link_roll:migrate VERSION=0
The version number corresponds to the number of your extension’s migration (file), not the version number in the schema_info table.
)
Generating a Controller
Now that we have the appropriate model object setup for our link role, let’s create the associated controller. Run the following command:
script/generate extension_controller LinkRoll admin/links
This should output:
create app/controllers/admin
create app/helpers/admin
create app/views/admin/links
create test/functional/admin
create app/controllers/admin/links_controller.rb
create test/functional/admin/links_controller_test.rb
create app/helpers/admin/links_helper.rb
If you look at the output of the command, you will notice that it generated:
- the Admin::LinksController
- the link controller helper
- the functional test for the controller
- the folder for the link controller views
Also note that we put the LinksController in the Admin module because it will be part of the admin interface.
Open up the controller file now (“vendor/extensions/link_roll/app/controllers/admin/links_controller.rb”). It should look like this:
class Admin::LinksController < ApplicationController
# Remove this line if your controller should only be accessible to users
# that are logged in:
no_login_required
end
Since this is an admin controller for links, remove the no_login_required line. For this example we are going to use scaffolding to create a quick editing interface, but nothing would hinder you from creating your own actions and associated views just like you would in a normal Rails application.
To tell Rails that we want to use scaffolding for the Link model object insert the following line in the class body of the LinksController:
scaffold :link
Your LinksController should now look something like this:
class Admin::LinkController < ApplicationController
scaffold :link
end
There are two remaining steps in order to get our LinkController to show up in the admin interface. First, we need to setup the routes definitions for the controller and second we need to add the associated tab to the admin interface.
To do this, open up the main extension file again (“vendor/extensions/link_roll/link_roll_extension.rb”). Change the define_routes block so that it looks like this:
define_routes do |map|
map.connect 'admin/links/:action', :controller => 'admin/links'
end
Change the activate method so that it looks like this:
def activate
admin.tabs.add "Links", "/admin/links", :before => "Layouts"
end
And the deactivate method so that it looks like this:
def deactivate
admin.tabs.remove "Links"
end
You can define any number of routes in the define_routes block—just like you can in the routes.rb file of a normal Rails application.
The activate and deactivate methods are called for an extension whenever it is activated or deactivated. Normally these events occur when an application starts or when an extension is turned off through the admin interface. We are using the methods to add the “Links” tab before the “Layouts” tab.
The LinkRollExtension class should now look like this:
class LinkRollExtension < Radiant::Extension
version "1.0"
description "Allows you to add a link roll to your Web site."
url "http://dev.radiantcms.org/radiant/browser/trunk/extensions/link_roll/"
define_routes do |map|
map.connect 'admin/links/:action', :controller => 'admin/links'
end
def activate
admin.tabs.add "Links", "/admin/links", :before => "Layouts"
end
def deactivate
admin.tabs.remove "Links"
end
end
If you setup your links controller and extension files correctly, you should now be able to create and edit new links from the admin interface. To test it out, run:
% script/server -e production
And go to “http://localhost:3000/admin” in your browser. Log in and click on the new “Links” tab. You’ll see a full featured editing interface for your new link roll (provided almost entirely through the builtin Rails scaffolding). Experiment a little with the interface. Add a couple of links to your link roll. Get a feel for how it all works together.
Creating Custom Tags
Now that we have an easy way to create and add links in the admin interface, it would be nice to add a couple of custom tags so that we can display links from our link roll on the Web site. Let’s do that now.
Create a new file in the “app/models” directory of your extension called “link_roll_tags.rb” and add the following code:
module LinkRollTags
include Radiant::Taggable
tag 'links' do |tag|
tag.expand
end
tag 'links:each' do |tag|
result = []
Link.find(:all, :order => 'title ASC').each do |link|
tag.locals.link = link
result << tag.expand
end
result
end
tag 'links:each:link' do |tag|
link = tag.locals.link
%{<a href="#{link.url}" title="#{link.description}">#{link.title}</a>}
end
end
Above, the LinkRollTags module includes the wonderful Radiant::Taggable module which adds the wonderful “tag” class method so that we can define our own tags in the module. (If you need access to Rails helpers in your tag, simply include them it the model: include TextHelper)
To make sure the tags are defined when our extension is activated, switch over to the main extension file (“link_roll_extension.rb”) and add the following line to the “activate” method:
Page.send :include, LinkRollTags
We’ve defined three tags for the Link module. In this tutorial we are not going to discuss the inner workings of the Radius tag DSL (for that you should read the Radius Quickstart guide), but suffice it to say that using the tag definitions above we can now write the following inside a page, layout, or snippet:
<ul>
<r:links:each>
<li><r:link /></li>
</r:links:each>
</ul>
An have it output something like:
You can now use the code above to manage a list of favorite sites on your blog:
Conclusion
This concludes the Creating Radiant Extensions tutorial. I hope you’ve gotten a feel for the many things that are possible with Radiant extensions. Of course, the LinkRoll extension in this tutorial could use a lot more work to make it a truly polished extension that you can be proud of, but hopefully I’ve given you enough of a head start that you can take it from here.




