Tuesday, May 18, 2010

Creating printable version of a web page


In web based applications, printing is a common requirement.User can print a web page by using the printing utilities of the browser.This may leave users at a surprised state because most of the time the format of the printed web page is messed up.For this reason a "Printer friendly" version of the page is provided. There are many ways to create a printer friendly version using server-side scripting. A separate request for printer friendly page is sent to the server and the server responses with a well formatted printable page. While these ways are completely workable, an elegant and client based approach is the use of only CSS to provide a printable version.

To accomplish this, we need to follow the next simple processes.

1. Create a seperate CSS file that controls the appearence of the page's elements for printable version.Let's call it "printable.css".
2. Include the CSS into the page that will be printed with the following syntax.

<link rel="stylesheet" href="--CSS folder path--" media="print" />

The important part here is the "media" attribute. When the value of this attribute is set to "print", the browser automatically uses this CSS file to format the page while printing.

Let's see a quick example. The layout of our page could be the following:

<html>
<head>
<link rel="stylesheet" href="<CSS folder path>/printable.css" type="text/css" media="print" />
</head>
<body>
<div id="header">
<!- HTML elements for header(logo,banner) -->
</div>

<div id="top_menu">
<!- HTML elements for top menu goes here -->
</div>

<div id="main_content">
<!- HTML elements for page content goes here -->
</div>

<div id="footer">
<!- HTML elements for footer goes here -->
</div>
</body>
</html>

While printing this page, we would probably want to print only the main content and escape the printing of the header, menu or footer. We can accomplish this by using the "printable.css" as follows:

#header,#top_menu, #footer {display:none}

We can add a link or button to anywhere in the page above to provide printing functionality.

<a href="#" onclick="window.print(); return false"> Print </a>

When user clicks on the "Print" link, the browser uses the "printable.css" and hides all portions except the content of the "main_content" element while printing.The result is a formatted printer friendly version of the page.We can fine tune the "printable.css" to take more control over the printable version.

Tuesday, March 9, 2010

Loading seed data with Seed-Fu plugin



Loding seed (default) data for lookup database tables is common to the most applications.Rails provide different mechanisms to accomplish this task. I have found the implementation with a plugin called seed-fu very simple and easy to implement.

Seed-Fu is based around loading ruby scripts located in db/fixtures via a Rake task.We just place the ruby files with the code to load seed data into the db/fixtures folder followed by executing the "db:seed" rake command .The plug in handles the rest.

Looking into the details of one of the seed data file will present a clearer view.We have a model named "Role" and want to populate seed data for this.We place a file named "roles.rb" into the "db\fixture" folder.The file name can be anything but "roles.rb" but we stick to the convention here.The file content as follows:


# # db/fixtures/roles.rb
# # put as many seeds as you like in

Role.seed(:name) do |s|
 s.name ='Admin'
 s.description = 'Administrator of the system'
end

Role.seed(:name) do |s|

 s.name ='Developer'
 s.description = 'Developer of a project'
end

Role.seed(:name) do |s|
 s.name ='Product Owner'
 s.description = 'Product owner of a product'
end


We want to add 3 roles (Admin,Developer and Product owner) so we put 3 "Role.seed" block and populate the "name" and "description" attributes.When we run "db:seed", the roles database table will be populated accordingly.

What will happen if we add some more seed data files later and run "db:seed" rake command? Will the role data will be duplicated? It would normally but we have taken a guard :)

Look at the parameter ":name" after the "Role.seed" method call in the above file.The "Role.seed" method takes a comma separated list of symbols that represent some of the key attribute names of the corresponding model ("Role" here).Before inserting a new row, seed-fu checks whether a row with the key values already exists.If a row exists, no data is inserted.

In the above example we have provided ":name" as the key column/attribute name.Before inserting the first "Role.seed" block,seed-fu conducts a search in the "roles" table for a row with the value "Admin" in "name" field.If a row exist, it skips the block(duplicate row not added) and try to proceed with the same rule for the next blocks.

Once you have set up your fixtures, it’s simple to run them:

rake db:seed

or

rake db:seed RAILS_ENV=<development| test | production>


Make sure to populate the database schema with "db:migrate" or "db:schema:load" before you run the "db:seed" command.

You can download the latest version of seed-fu plugin from GitHub .

Friday, February 5, 2010

Rails cache expiration with sweepers



Sweepers enable expiring or deleting the rails cached items from a centralized place.This exempt us from the pain of writing cache expiration codes scattered throughout the code base by providing an implementation of the observer pattern.Sweeper classes observe one or more models that contributes to the cache data and provide options to expire the related cached items when any changes occur in those models(created,updated or deleted).

Let's understand the process with an example.We have a model named "SystemMessage" that provides messages posted by the system administrator.In the main layout we display the count of total system messages.Since several views are rendered inside this layout, we need to hit the database every time to get the count.To avoid this we decide to cache the count during the first database hit.We cache the count in the following code placed inside a method of the application_controller.

Rails.cache.fetch("systen_message_count") {SystemMessage.count}


The code above hits the database to fetch the count at the very first access and cache it with the key "systen_message_count". Subsequent calls to the method returns the count from the cache until it is expired.

Now we need to expire and reload the count when a new "SystemMessage" is created or an existing one is deleted.Instead of putting the cache expiration code in different places inside controllers and models, we can put it inside a single sweeper class and react to the changes in the "SystemMessage" model(creation or deletion).Here is the code placed inside the "\app\models\cache\system_messages_sweeper.rb" file.

class SystemMessagesSweeper < ActionController::Caching::Sweeper

observe SystemMessage

def after_create(record)
 expire_system_message_cache(record)
end

def after_destroy(record)
 expire_system_message_cache(record)
end

private

def expire_system_message_cache(record)
  Rails.cache.delete("systen_message_count")
end

end

In the code above, we observe the "SystemMessage" model and expire the system_message_count" cache item when a new record is created or deleted.So the count
will be loaded again from the database at the next hit and will be cached subsequently.

The sweeper class needs to be loaded by the rails.So we put the following code into the environment.rb file

config.load_paths += %W( #{RAILS_ROOT}/app/models/cache )


The sweeper has to be added to the controller that will use it. The following code is added to the "SystemMessagesController" class.

cache_sweeper :system_messages_sweeper, :only => [ :create,destroy ]


Rails cache sweepers have played an important role in improving the performance of ScrumPad, which is a popular agile/scrum project management and collaboration tool.