Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Ruby

Ruby on Rails on Windows in production

4.67/5 (2 votes)
2 Feb 2012CPOL13 min read 22.8K  
This article is primarily aimed at rookie web-programmers and those who are thinking of sticking to Ruby on Rails.

More and more Windows-developers cast a glance at Ruby – dynamic high-level programming language. One of the reasons of this interest is a framework for web applications – Ruby on Rails. Developing with Rails is easy and fun. Due to high dynamics of Ruby developers get a wide range of additional tools, and with rather few lines of code you can have rich functionality. This article is primarily aimed at rookie web-programmers and those who are thinking of sticking to Ruby on Rails. The following issues will be outlined below:

  • Installing work environment
  • Writing a simple app
  • Deploying the app on the server

As a resume you’ll see the results of the tests contrasting performance of Ruby-apps run (in different modes) on Windows and Ubuntu.

Installing work environment

First, we need to download and install WebPlatformInstaller, run it, click Options and add the link to HeliconZooFeed: http://www.helicontech.com/zoo/feed/ into the «Displayadditionalscenarios» field.

In result, WebPI will add Zoo tab with «Applications», «Packages», «Modules» and «Engines» sections.

Installing Ruby on Rails

In Engines section you can choose engines to be installed including Ruby on Rails 3.1 and 2.3. Ruby version 1.8 and 1.9 are also available. By default Rails 3.x installs Ruby 1.9 as a dependency, while Rails 2 will install Ruby 1.8.

For convenience the Packages section offers Ruby Hosting Package which includes the following components:

  • Ruby 1.8.7 andRuby 1.9.3;
  • Rails 2.3.11, 3.0.10 and 3.1;
  • RubyDevKit (for assembling C-extensions);
  • Popular gems: mysql2, pg, sqlite3, mongo_mapper, tiny_tds, activerecord-sqlserver-adapter, devise, authlogic, jekyll, formtastic, will_paginate, Sinatra, ramaze, camping-omnibus, Thin, Goliath.

Creating your first web-site

A convenient way to create new web-app is using WebMatrix and IIS Express. On Zoo tab, under Packages section in WebPI you can find WebMatrix Templates package, offering different templates including the one for Ruby on Rails.

If you don’t yet have WebMatrix, it will be automatically installed with the WebMatrix Templates package. Keep in mind that downloading and installing all packages may take some time. Having templates installed run WebMatrix and on the main page select Sit from template and then Rails Site.

After you created the Rails Site, follow the given URL or press Run to see the page with instructions.

The template is designed for development of Rails 2.3, 3.0 or 3.1 apps. At the moment there’s no application under the site folder, only configuration files. Let’s create it using the command:

rails new .

If you don’t explicitly specify the Rails version to use, it will use the latest available one. As in our case we installed Ruby Hosting Package, which includes three Rails versions and the latest of them is 3.1, the app template will be created based on Rails 3.1.

If we now refresh the page, we’ll see standard Ruby on Rails welcome page:

Development environment

For code editing you’ll need an editor or dev environment. If you prefer IDE with debugger and refactoring tools, consider one of the following options:

Each of these offers a bunch of development and testing tools for Ruby apps and supports version control.

For those who will make do with simpler solutions there are:

For our app we used Aptana:

MVC

Ruby on Rails is based on the MVC architecture (model, view, controller). This approach has several advantages:

  • Isolation of business logic from interface;
  • Less code repetition (DRY principle — don’t repeat yourself);
  • Relatively simple development hanks to strict code distribution based on purpose (e.g. the code for HTML form output is not mixed with the code for DB processing).

Model defines the database structure in terms of object-oriented programming. In Rails model is an ordinary class inheriting all necessary functions from ActiveRecord::Base class. An instance (object) of this class defines one line from the corresponding database. Thus, models conceal the peculiarities of interaction with particular DBMS from developer.

View is the interface shown to users. On this stage developer creates templates which are transformed into HTML, CSS, or JavaScript code.

Controller connects model with view. It’s usually controller that contains the main logic. Essentially, controllers are Ruby classes. Each public method of controller is called action. If you have controller named “Home” and it contains the method named “index”, then running /home/index in browser will evoke “index” action.

When a request comes to the application, the routing mechanism (in the config/routes.rb file) decides which controller cares about that type of requests. Aside from the URL a set of other conditions may be taken into consideration, e.g. you can assign different controllers for different browsers, for mobile clients etc.

So, having chosen the controller, it defines what action will process the request. At this point one can also apply numerous conditions. The action itself performs some calculations and DB-related operations. When the action is finished the view comes on the scene. The data from DB or some result are transmitted to the templates. Then the HTML page is generated from the templates (there are templates from CSS and JavaScript as well) and the response page in sent to the user.

Writing the application

The classic example of Rails app is the simplest blog. We won’t ignore this tradition. So, let’s delete the welcome file public/index.html and create controller “Home” with action “index” – this is going to be the blog main page. This is done with the following line run from the app folder:

rails g controller home index

If we now request /home/index, we’ll get the page from the template created for “index” action.

Afterwards, we’ll create a simple Post model which will define each blog entry in the DB. In Ruby code the model is a class and it is represented in DB as a table. Thus, the object of Post class is a line in the corresponding table in database.

To create model you can simply run “rails g model Post…” but let’s make use of a very handy tool – scaffolding. The “rails g scaffold” command creates not only the model class itself and tests for it, but also actions drafts and views templates for adding, editing and removing model objects. If we execute this

rails g scaffold Post name:string title:string content:text

we’ll get “Post” model in app\models\post.rb, “Posts” controller in app\controllers\posts_controller.rb with actions index, show, new, edit, update, create and destroy, plus a DB migration scenario in db\migrate. The command will also have created the headers for tests and HTML templates. Notice that we haven’t yet written a single line of code!

Next, we’ll run the command to create the database (if it’s not yet created) and the table “posts” with the fields “name”, “title”, and “context”:

rake db:migrate

The command for database migration is applied for creating and editing database structure in accordance with our object model. It should be executed every time you make changes to the application model. All the magic of matching the DB structure with our model is done automatically and all the data stored in the database are retained.

FYI, in this example we use SQLite which is default for Rails. However Rails can work with many other DBMS hiding details of their cooperation from the user.

Actions of “Post” controller are accessible at /posts/ address.

If you press “New post”, you’ll see the form:

After filling all fields we get to the new post page:

Note that there was still no code written. Now let’s do some editing. For instance, we may need to make post name and post title obligatory fields so that corresponding cells in the DB are always non-empty. Luckily, Rails provides very simple validation mechanism. We should just fix the model file as follows:

Ruby
class Post < ActiveRecord::Base
      validates :name,  :presence => true
      validates :title, :presence => true,
                :length => { :minimum => 5 }
end

In there we specify that “name” and “title” fields are obligatory and “title” should contain not less than 5 characters. No need to apply migration after this change as validators are not directly related to database; the check is performed on the level of Ruby code.

If you now leave the “name” field empty, you’ll get an error:

Let’s make it a little more complicated and add the comments feature. We’ll create a “Comment” model with the following command:

rails g model Comment commenter:string body:text post:references

Pay attention to the “post:references” parameter. It connects «comments» table with “posts” table.

Now refresh the database:

rake db:migrate

Next, we’ll set up the relation “has many” for Post model:

Ruby
class Post < ActiveRecord::Base
      validates :name,  :presence => true
      validates :title, :presence => true,
                :length => { :minimum => 5 }

      has_many :comments, :dependent => :destroy
end

The code is intuitively clear. Each Post object may have a number of comments. “:dependent => :destroy” tells that when post is deleted all comments are deleted as well. As we didn’t use scaffolding this time to create a model for comments, we now need to generate corresponding controller:

rails g controller Comments

In config\routes.rb file replace “resources :posts do” with:

Ruby
resources :posts do
   resources :comments
end

In this way we specify how the “comments” controller will be available. In our case it’s put into “posts”, so the links will look like http://localhost:41639/posts/1/comments/3.

Then we need to refresh the template app\views\posts\show.html.erb so that it will become possible to add comments. After:

XML
<p>
  <b>Content:</b>
  <%= @post.content %>
</p>

add the following code:

XML
<h2>Comments</h2>
<% @post.comments.each do |comment| %>
  <p>
    <b>Commenter:</b>
    <%= comment.commenter %>
  </p>
  <p>
    <b>Comment:</b>
    <%= comment.body %>
  </p>
  <p>
    <%= link_to 'Destroy Comment', [comment.post, comment],
                :confirm => 'Are you sure?',
                :method => :delete %>
  </p>

<% end %>

<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
  <div class="field">
    <%= f.label :commenter %><br />
    <%= f.text_field :commenter %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Finally, we’ll define the logic of controller operation in app\controllers\comments_controller.rb

Ruby
class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = @post.comments.create(params[:comment])
    redirect_to post_path(@post)
  end

  def destroy
    @post = Post.find(params[:post_id])
    @comment = @post.comments.find(params[:id])
    @comment.destroy
    redirect_to post_path(@post)
  end
end

And everything is ready for adding comments to the post:

The basic functionality is implemented. As the last step we’ll protect some of the actions, so that unwanted people don’t have access to them. The more comprehensive way is to use registration, sessions, cookies etc., but to stay simple we’ll take Basic authentication, the more so because in Rails we need only one line to enable it. Put the following in posts_controller.rb:

Ruby
http_basic_authenticate_with :name => "admin", :password => "123",
                              :except => [:index, :show]

We have hard coded the login and password. “:except” parameter excludes “:index” and “:show” actions as they don’t need authentication.

Deploying on server

So, we’ve created the application and now, logically, want publish it to Internet. For that purpose we’ll set up Windows server to work with Rails in production. We’ll have to repeat several steps from the beginning of the article which were used to install development environment:

  1. install Microsoft Web Platform Installer;
  2. add Helicon Zoo feed;
  3. install Ruby Hosting Package from Zoo repository.

Before installation make sure WebPI is set up to install applications for IIS and not for IIS Express. After these three steps are completed the server is ready to host our application. For the moment the following server platforms are supported: Windows Server 2008 and 2008 R2, 32-and 64-bit versions.

So, first, we create an empty web-site via IIS manager or your hosting panel. Then upload your application to the server via FTP or WebDeploy. In the case with WebDeploy, the folders will be also given proper permissions automatically, if needed. You can also use Git or any other version control system and deployment system, but that falls beyond the scope of this article.

The application will be executed on Windows Server under IIS with the help of Helicon Zoo Module. This module was initially designed as a hosting solution, so all applications are isolated and do not interfere. The module with its default options works in automatic mode, creating one worker process by default, when the load is low and increasing the number of workers up to the number of CPU cores of the server providing maximal performance under heavy loads.

Helicon Zoo applies the concept of engines and applications. The engines section define what to run and how, on which protocol and port, what maximal number of workers are allowed and other global settings which are defined in applicationHost.config. Then, under the site you can create an application using specific engine and send all parameters necessary for this app’s operation to it. This concept allows for separation of hosting administrator duties from the clients’ burden (and client from client as well). You can learn more about Helicon Zoo Module and its settings here: http://www.helicontech.com/zoo/module.htm

Please pay attention to deploy.rb file in the root of the site and DEPLOY_FILE and DEPLOY_LOG parameters in web.config. Zoo executes deploy.rb upon each start of IIS application pool. This approach may appear especially helpful for those who don’t have administrator privileges on the server. As for deployment you’ll probably need to do “bundle install”or apply database migration, you can simply restart your application on the server and deploy.rb will be executed automatically. There is a pre-defined deploy.rb file in the Ruby on Rails application template with most popular commands.

It’s worth mentioning that deploy.rb script is executed by the same Windows user that runs IIS application pool. As a rule this user has rather limited permissions that should be accounted for when you write your commands. As an example, ordinary “bundle install” command may lead to error because the user is not allowed to write directly into Ruby installation folder. Way out: save all gems under the site in a special folder “vendor/gems” by executing “bundle package” command in the application folder before sending it to the server. And in deploy.rb put the command “bundle install –local –path vendor/gems”.

The errors occurring for deploy-script may be logged in the file specified in DEPLOY_LOG environment variable.

The last issue to point out during deployment is RACK_ENV variable in web.config. In the web.config created from WebMatrix template the RACK_ENV variable contains the value “development”. This enables Rails in the mode more suitable for development. When you put the application on the server, change the value to “production”. BTW, “production” is actually default.

Performance tests

Machine used as a server contained Core 2 Quad 2.4 GHz, 8 Gb RAM, 1GB LAN. For load generation we took more powerful PC and Apache Benchmark tool with a command “ab.exe -n 100000 -c 100 –k”. For Apache and Nginx servers we used Ubuntu 11.04 Server x64. IIS 7 tests were run on Windows Server 2008 R2. No virtual machined where used – bare hardware.

We’ve conducted three test scenarios:

  1. Rails had to output the current time in high resolution. The time was chosen to ensure the response doesn’t come from cache;
  2. Rails read from MySQL database and;
  3. Rails performed writing into MySQL  database.

We performed tests with Ruby 1.9.3, Rails 3.1.1 and MySQL 5.1.54. in case of HTTP transport, Thin acted as a backend HTTP service. Neither Unicorn nor Passenger work for Windows. So, there were three configurations for testing: Windows + IIS 7.5 + Helicon Zoo + Thin, Ubuntu + Apache + Passanger, Ubuntu + Nginx + Thin.

Below are tests results (in requests per second):

  

Here are more detailed ab graphs for the first test (time output):

Resume

Ruby on Rails proved itself a perfect framework for quick and easy web development. Of course, Ruby is not the one and only. In the next articles, we’ll shed some light on Goliath and Sinatra.

We would also like to underline that Windows is a mighty platform for both development with Ruby and running Ruby-apps in production. And if earlier the difference in Ruby performance on Linux and Windows was dramatic, now the performance, as well as convenience of Ruby on Rails for Windows has significantly improved to a degree that performance is no more a key criteria for choosing the platform.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)