24
Jun 09

Easy Ruby on Rails Deployment with Capistrano and Passenger

Let’s start off with a few aliases that speed things up. The commands are pretty self explanatory. I’ve got my deployment down to a simple acd command, which will add and commit to svn, then deploy to the server. To add a migration before deployment I use acmd. The following commands go into your ~/.bash_login file (assuming you’re using OS X or Linux).

1
2
3
4
5
6
7
alias svna='svn add * --force'
alias svnc='svn commit . -m ""'
alias capd='cap deploy'
alias capm='cap deploy:migrate'
alias acd='svna; svnc; capd;'
alias acmd='svna; svnc; capm; capd;'
alias rors='script/server -b localhost'

If you don’t already have capistrano setup, it’s as easy as

1
$ gem install -y capistrano

When I first setup an new rails project I follow these steps

1
2
3
4
5
6
7
8
$ rails -d mysql PROJECT_NAME
$ cd PROJECT_NAME
$ capify .
$ svn import PROJECT_PATH SVN_PATH -m 'Initial import'
$ cd ..
$ rm -Rf PROJECT_PATH
$ svn co SVN_PATH
$ mate PROJECT_PATH

This initial setup process will create the rails project, setup Capistrano, add it to svn, and open it in TextMate. You can of course leave that last line out and use any IDE you wish.

Next open the config/deploy.rb file that Capistrano created for you. This is where you will put all your Capistrano instructions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
set :user, "SUDO_USER"
set :password, "SUDO_USER_PASS"
set :domain, "example.com"
server domain, :app, :web
role :db, domain, :primary => true
 
set :application, "example"
set :deploy_to, "/home/example/public_html/#{application}.sandbox.#{domain}/#{application}/"
 
default_run_options[:pty] = true
set :use_sudo, true
set :runner, :user
 
set :repository,  "http://#{domain}/svn/#{application}"
set :scm_username, "SVN_USER"
set :scm_password, "SVN_USER_PASS"
set :checkout, "export"
 
namespace :deploy do
  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end
end

Just change all the capitalized tokens to reflect your own settings. Then deploying should be as simple as typing acd in the Terminal. I created a blank file at /tmp/restart.txt and added it to svn so passenger will restart the rails app when you deploy. Also, you may not want to put your sudo password into deploy.rb.

I entountered various struggles in getting everything to work correctly. If you need help with something specific, post it in a comment. I can probably tell you what the problem is.

You can find help for Passenger here: http://www.modrails.com/ and help for Capistrano here: http://www.capify.org/

18
Jun 09

Initializing SubSonic in your DNN module

SubSonic requires you to put some stuff in the web.config. You may be wondering how you’re going to distribute your module without requiring the buyer to alter his web.config. Here’s the method that I use:

1
2
3
4
5
6
7
8
9
10
11
12
private static void InitializeProvider()
{
    DataService.Provider = new SqlDataProvider();
    DataService.Providers = new DataProviderCollection();
    DataProvider provider = DataService.Provider;
    var config = new NameValueCollection();
    config.Add("SubSonicSqlString", ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString);
    provider.Initialize("SubSonicSqlString", config);
    DataService.Provider.DefaultConnectionString = ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString;
    DataService.Provider.GeneratedNamespace = "Example.DataModel";
    DataService.Providers.Add(provider);
}

This method goes into my Page_Init event. Luckily I setup my module pages to inherit a base class that does all my setup. Here’s the base class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web.UI.HtmlControls;
using DotNetNuke.Entities.Modules;
using SubSonic;
 
namespace Example.Common
{
    public class ModuleBase : PortalModuleBase
    {
        #region Events
 
        protected void Page_Init(Object sender, EventArgs e)
        {
            InitializeProvider();
        }
 
        #endregion
 
        #region Methods
 
        private static void InitializeProvider()
        {
            DataService.Provider = new SqlDataProvider();
            DataService.Providers = new DataProviderCollection();
            DataProvider provider = DataService.Provider;
            var config = new NameValueCollection();
            config.Add("SubSonicSqlString", ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString);
            provider.Initialize("SubSonicSqlString", config);
            DataService.Provider.DefaultConnectionString = ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString;
            DataService.Provider.GeneratedNamespace = "Example.DataModel";
            DataService.Providers.Add(provider);
        }
 
        protected void RegisterJavascript(string fullPath)
        {
            var script = new HtmlGenericControl("script");
            script.Attributes.Add("type", "text/javascript");
            script.Attributes.Add("src", fullPath);
 
            Page.Header.Controls.Add(script);
        }
 
        #endregion
    }
}

In my last post about including jQuery properly I introduced the RegisterJavascript method. We can put the method in this class so we can use it in all pages inheriting the class.

One thing worth mentioning is that the app.config for SubSonic must be setup before you can expect this to work. You should also have a SubSonic data model built and referenced. You can learn more about that here: http://michaelpardo.com/2009/06/setting-up-subsonic-in-you-dnn-module-solution/

18
Jun 09

Setting up SubSonic in your DNN module Solution

In my modules I generally have three different projects. One for SubSonic, one for common stuff, and one for the module itself. The first thing I do when setting up a module is add a project for SubSonic. Let’s add a “Class Library” and call it “DataModel”.

Add New Project

Add New Project

I’ll get to the setup of the other projects in another post. Let’s just worry about SubSonic for right now as it is probably the easiest project to setup.

Now that we have the project added let’s add our app config. From the “Project” menu click “Add New Item…”. Select “Application Configuration File” and add it to your project.

Add New Item

Add New Item

Here is the blank app.config file that I use to get started. You will need to alter some of the settings to fit your project.

You can find more information on setting up the app.config here: http://subsonicproject.com/setup/gettingstarted/

After you have done the initial setup you will be able to generate your classes. If you haven’t already setup SubSonic in Visual Studio you can learn how here: http://michaelpardo.com/2009/06/visuals-studio-and-subsonic/

Click “Tools” and then “SubSonic” to generate the classes. You will not see the files in the Solution Explorer until you click “Show All Files” and “Refresh”. If you have done all these steps properly you will see a “Generated” folder in your project. Add the folder and build.

You’re done! Now you can reference this project in your module.

17
Jun 09

Including jQuery in DotNetNuke

As of DNN 5, jQuery is now included. This is great, unless you’re distributing a module and you want it to work with versions below 5 also. Fortunately there is a way to check if jQuery has already been included, thereby avoiding conflicts.

Notice the code is placed in the Page_PreRender event. This is an absolute must.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void Page_PreRender(object sender, EventArgs e)
{
	if (HttpContext.Current.Items["jquery_registered"] == null)
	{
		// load jQuery
		RegisterJavascript("/DesktopModules/MyModule/js/jquery.js");
		// let other modules know about jQuery
		HttpContext.Current.Items.Add("jquery_registered", "true");
	}
 
	// load all the plugins
	RegisterJavascript("/DesktopModules/MyModule/js/jquery.foo.js");
	RegisterJavascript("/DesktopModules/MyModule/js/jquery.bar.js");
}

Here’s the RegisterJavascript method I use.

1
2
3
4
5
6
7
8
protected void RegisterJavascript(string fullPath)
{
	var script = new HtmlGenericControl("script");
	script.Attributes.Add("type", "text/javascript");
	script.Attributes.Add("src", fullPath);
 
	Page.Header.Controls.Add(script);
}

Now you can continue using jQuery without any worries of conflict.

One mistake that had me tripped up for a while was an include of jQuery in the skin. I didn’t realize it was there and I couldn’t figure out why none of my plugins worked. As a general rule I never include jQuery in the skin.

17
Jun 09

Visual Studio and SubSonic

If you aren’t using SubSonic yet, you should be. You can find out more at subsonicproject.com. Here’s the easiest way to get SubSonic working smoothly from within Visual Studio.

Download the SubSonic source from http://subsonicproject.googlecode.com/files/SubSonic_2.1_Final_Source.zip and extract it. I chose to create a folder for SubSonic in “C:\Program Files\”.

SubSonic Folder

SubSonic Folder

Next, in Visual Studio click the “External Tools…” menu item from the “Tools” menu. A dialog box will pop up allowing you to manage the external tools. Click “Add” and enter the following:

External Tools Dialog

External Tools Dialog

Title: SubSonic (or whatever you want really)
Command: C:\YOUR_SUBSONIC_PATH\SubSonic2.2\SubCommander\sonic.exe
Arguments: generate /out $(ProjectDir)/Generated
Initial directory: $(ProjectDir)

I check “User Output window”. If you don’t a console window will popup every time you run the command.

Now you will have a “SubSonic” menu item in your “Tools” menu which will generate all your subsonic classes.

Tools Menu

Tools Menu

Of course, you need to have your app.config/web.config setup with SubSonic for the classes to generate. You can find more information about doing that here: http://subsonicproject.com/setup/gettingstarted/.