CSC/ECE 517 Fall 2010/ch3 3a LG: Difference between revisions
No edit summary |
No edit summary |
||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=Web Application Frameworks= | =Web Application Frameworks= | ||
==Introduction== | ==Introduction== | ||
What's a web application framework? What is a framework? A framework is an underlying structure [1], and a web application framework is just that. Commonly web application frameworks are associated with agile/fast development; this characteristic is attached to the Model-View-Controller (MVC) architectural pattern. MVC is, by far, the most popular pattern used in web application frameworks. According to Wikipedia [2] content management systems (CMS) are also considered web application frameworks, but we will leave those aside. | What's a web application framework? What is a framework? A framework is an underlying structure [[#References|[1]]], and a web application framework is just that. Commonly web application frameworks are associated with agile/fast development; this characteristic is attached to the Model-View-Controller (MVC) architectural pattern. MVC is, by far, the most popular pattern used in web application frameworks. According to Wikipedia [[#References|[2]]] content management systems (CMS) are also considered web application frameworks, but we will leave those aside. | ||
Before web application frameworks, websites that dealt with dynamic content were often written from scratch and a page at a time, each page was responsible for handling its own requests. Websites were rigid and code was repeated everywhere (SQL operations, parameter handling, etc.). One step backwards, websites didn't include dynamic content at all and were written entirely in static HTML files, leaving the responsibility of updating the site to developers. | Before web application frameworks, websites that dealt with dynamic content were often written from scratch and a page at a time, each page was responsible for handling its own requests. Websites were rigid and code was repeated everywhere (SQL operations, parameter handling, etc.). One step backwards, websites didn't include dynamic content at all and were written entirely in static HTML files, leaving the responsibility of updating the site to developers. | ||
In this chapter we will study four web application frameworks. This chapter was written in a “new web application frameworks for Ruby on Rails developers” way, showing Rails' way first and comparing the rest of the web frameworks with it. | In this chapter we will study four web application frameworks. This chapter was written in a “new web application frameworks for Ruby on Rails developers” way, showing Rails' way first and comparing the rest of the web frameworks with it. However its knowledge is not necessary to comprehend the topics being discussed. | ||
The first section will present the frameworks being examined, a brief description and a list of key points they show at their websites. | The first section will present the frameworks being examined, a brief description and a list of key points they show at their websites. Immediately after we'll examine the models, view and controllers of the Cookbook application written in all four web frameworks. | ||
==Another Gang of Four… Not Really! Four Web Application Frameworks== | ==Another Gang of Four… Not Really! Four Web Application Frameworks== | ||
Line 29: | Line 29: | ||
|- | |- | ||
| Logo | | Logo | ||
| | | Logo can be found in http://commons.wikimedia.org/wiki/File:Ruby_on_Rails_logo.jpg | ||
|} | |} | ||
Ruby on Rails is the oldest of the four web applications frameworks presented; it was initially released in 2004 [3], the rest were released anytime in 2005 [4], [5], [6]. It's so popular that CakePHP was admittedly inspired on it ([7]) and many other web frameworks were also inspired by its success. Rails openly follow the convention over configuration (COC) paradigm as one of its main tenets [3]. Other languages don't mention this, but as far as I'm concerned they all follow this paradigm; it's the COC paradigm that allows writing entire applications in few lines of code. | Ruby on Rails is the oldest of the four web applications frameworks presented; it was initially released in 2004 [[#References|[3]]], the rest were released anytime in 2005 [[#References|[4]]], [[#References|[5]]], [[#References|[6]]]. It's so popular that CakePHP was admittedly inspired on it ([[#References|[7]]]) and many other web frameworks were also inspired by its success. Rails openly follow the convention over configuration (COC) paradigm as one of its main tenets [[#References|[3]]]. Other languages don't mention this, but as far as I'm concerned they all follow this paradigm; it's the COC paradigm that allows writing entire applications in few lines of code. | ||
====Key points==== | ====Key points==== | ||
Listed as the “basic tenets of Ruby on Rails” [3]. | Listed as the “basic tenets of Ruby on Rails” [[#References|[3]]]. | ||
* Convention over Configuration | * Convention over Configuration | ||
* Don't Repeat Yourself (DRY) | * Don't Repeat Yourself (DRY) | ||
Line 59: | Line 59: | ||
|- | |- | ||
| Logo | | Logo | ||
| | | [[Image:Django_logo.png]] | ||
|} | |} | ||
Django is a “high-level [5]” web application framework written in Python. It follows the MVC architectural pattern and focuses on automating and adhering to the DRY principle. It was conceived in an online-news operation to handle two challenges: intensive deadlines and “stringent requirements of the experienced Web Developers who wrote it. [5]” | Django is a “high-level [[#References|[5]]]” web application framework written in Python. It follows the MVC architectural pattern and focuses on automating and adhering to the DRY principle. It was conceived in an online-news operation to handle two challenges: intensive deadlines and “stringent requirements of the experienced Web Developers who wrote it. [[#References|[5]]]” | ||
One of the big things in Django is its “automatic admin interface.” Other frameworks can achieve this by including third party components/plugins, but the fact that Django includes it by default is a plus. Authentication and authorization, using access control list (ACL), are added to your application, almost without intervention from the developer. | One of the big things in Django is its “automatic admin interface.” Other frameworks can achieve this by including third party components/plugins, but the fact that Django includes it by default is a plus. Authentication and authorization, using access control list (ACL), are added to your application, almost without intervention from the developer. | ||
====Key points==== | ====Key points==== | ||
Listed in an untitled list on their website [5]. | Listed in an untitled list on their website [[#References|[5]]]. | ||
* Object-relational mapper (ORM) | * Object-relational mapper (ORM) | ||
*Automatic admin interface | *Automatic admin interface | ||
Line 88: | Line 88: | ||
|- | |- | ||
| Logo | | Logo | ||
| | | [[Image:cakephp_logo.png]] | ||
|} | |} | ||
CakePHP is a “rapid [4]” development framework written in PHP. | CakePHP is a “rapid [[#References|[4]]]” development framework written in PHP. It uses the design patterns MVC and ORM. As stated before, CakePHP was inspired on the success of Ruby on Rails [[#References|[7]]]. One thing not mentioned on the “hot features” is that CakePHP supports dynamic scaffolding [[#References|[7]]]. Rails originally included this feature, but as for Rails 2.0 dynamic scaffolding is no longer supported [[#References|[3]]]. Dynamic scaffolding allows the user to test its Create, Read, Updated, and Delete (CRUD) functions by adding one line of code in their controller: | ||
<pre>var $scaffold;</pre> | <pre>var $scaffold;</pre> | ||
Line 98: | Line 98: | ||
====Key points==== | ====Key points==== | ||
Listed as “Hot Features” on [4]. | Listed as “Hot Features” on [[#References|[4]]]. | ||
* No configuration | * No configuration | ||
* Extremely Simple | * Extremely Simple | ||
Line 120: | Line 120: | ||
|- | |- | ||
| Logo | | Logo | ||
| | | Logo can be found in http://commons.wikimedia.org/wiki/File:Catalyst_logo3.png | ||
|} | |} | ||
Line 126: | Line 126: | ||
====Key points==== | ====Key points==== | ||
Or “What makes Catalyst special?” as they like to call them [6]. | Or “What makes Catalyst special?” as they like to call them [[#References|[6]]]. | ||
* Keep it Simple, Stupid | * Keep it Simple, Stupid | ||
* Don't Repeat Yourself | * Don't Repeat Yourself | ||
Line 140: | Line 140: | ||
====Ruby on Rails==== | ====Ruby on Rails==== | ||
Rails does a good job making its code readable, apart from the to_s method. | Rails does a good job making its code readable, apart from the to_s method. Defaults errors are shown if a validation fails and a dynamic variable is created to hold is parent Category. | ||
This model has no intervention on defining the table on the database. To be fair with Django, additional to this class, the CreateRecipes class (db/migrate) is shown. However that code was written almost entirely automatically by the generator so one could argue that it's not necessarily a fair comparison. The command “rake db:migrate” creates the database on the table based on the contents of the db/migrate folder. | This model has no intervention on defining the table on the database. To be fair with Django, additional to this class, the CreateRecipes class (db/migrate) is shown. However that code was written almost entirely automatically by the generator so one could argue that it's not necessarily a fair comparison. The command “rake db:migrate” creates the database on the table based on the contents of the db/migrate folder. | ||
Line 156: | Line 156: | ||
class CreateRecipes < ActiveRecord::Migration | class CreateRecipes < ActiveRecord::Migration | ||
def self.up | |||
create_table :recipes do |t| | |||
t.string :title, :limit => 255 | |||
t.string :description, :limit => 255 | |||
t.text :instructions | |||
t.integer :category_id | |||
t.timestamps | |||
end | |||
end | |||
def self.down | |||
drop_table :recipes | |||
end | |||
end | end | ||
</pre> | </pre> | ||
====Django==== | ====Django==== | ||
Being a neophyte in Python this code looked strange at first sight. While writing the code I had as many indentation errors as semicolons I tried to place in Ruby when I first learned it. The code is not as readable as in Rails, but it's short. Running the command “python manage.py syncdb” synchronizes the database structure with the contents of this file. | Being a neophyte in Python this code looked strange at first sight. While writing the code I had as many indentation errors as semicolons I tried to place in Ruby when I first learned it. The code is not as readable as in Rails, but it's short. Running the command “python manage.py syncdb” synchronizes the database structure with the contents of this file. Related objects and validations are also created. All models are defined in a single file called models.py. | ||
<pre> | <pre> | ||
Line 189: | Line 189: | ||
====CakePHP==== | ====CakePHP==== | ||
CakePHP might look a lot more like Java | CakePHP might look a lot more like Java or other widely known static languages (it's still a dynamically typed language, I'm comparing just in the syntax), just add “$” to each variable an treat arrays as both an Array and a HashTable. When putting the code side by side with Django and Ruby on Rails it looks verbosely. On top of that, there is no built-in database migration to keep a definition of the database inside the project. This is compensated by many components available at the Bakery [[#References|[8]]]. | ||
<pre> | <pre> | ||
Line 212: | Line 212: | ||
====Catalyst==== | ====Catalyst==== | ||
The tables need to be created directly on the database before generating the model. This model is generated by running the following script [9]. | The tables need to be created directly on the database before generating the model. This model is generated by running the following script [[#References|[9]]]. | ||
<pre> | <pre> | ||
script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ | script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ | ||
create=static dbi:SQLite:myapp.db \ | |||
on_connect_do="PRAGMA foreign_keys = ON" | |||
</pre> | </pre> | ||
The script reads the database and generates the following code. Neither the script, nor the code is readable. | The script reads the database and generates the following code. Neither the script, nor the code is readable. It verboseness is extravagant, which I think is a result of PERL's age. | ||
<pre> | <pre> | ||
Line 231: | Line 231: | ||
__PACKAGE__->table("recipes"); | __PACKAGE__->table("recipes"); | ||
__PACKAGE__->add_columns( | __PACKAGE__->add_columns( | ||
"id", | |||
{ data_type => "INTEGER", is_nullable => 0, size => undef }, | |||
"title", | |||
{ data_type => "VARCHAR", is_nullable => 0, size => 255 }, | |||
"description", | |||
{ data_type => "VARCHAR", is_nullable => 0, size => 255 }, | |||
"instructions", | |||
{ data_type => "text", is_nullable => 0 }, | |||
"created_at", | |||
{ data_type => "DATETIME", is_nullable => 0 }, | |||
"updated_at", | |||
{ data_type => "DATETIME", is_nullable => 0 }, | |||
); | ); | ||
__PACKAGE__->set_primary_key("id"); | __PACKAGE__->set_primary_key("id"); | ||
__PACKAGE__->belongs_to( | __PACKAGE__->belongs_to( | ||
); | |||
); | |||
1; | 1; | ||
Line 261: | Line 258: | ||
Rails does a good job keeping the controller slim. The convention over configuration is also shown here were format.html renders the index.html.erb file by default. The method responds to the GET action under /recipes because of pre-define routes created when scaffolding. | Rails does a good job keeping the controller slim. The convention over configuration is also shown here were format.html renders the index.html.erb file by default. The method responds to the GET action under /recipes because of pre-define routes created when scaffolding. | ||
<pre> | <pre> | ||
# GET /recipes | |||
def index | |||
@recipes = Recipe.all | |||
respond_to do |format| | |||
format.html | |||
end | |||
end | |||
</pre> | </pre> | ||
====Django==== | ====Django==== | ||
Just because Rails did a good job doesn't mean that Django wouldn't. | Just because Rails did a good job doesn't mean that Django wouldn't. They actually have a package named “shortcuts” which holds many DRY-oriented functions that perform common tasks. Something unique in Django is that this functional code is actually defined in the views.py file. And what we know as views is defined in templates. Just as in models, all controller functions are defined in a single file, keeping the file count small. | ||
</pre> | </pre> | ||
from django.shortcuts import render_to_response, … | from django.shortcuts import render_to_response, … | ||
… | … | ||
def index(request): | def index(request): | ||
recipes = Recipe.objects.all() | recipes = Recipe.objects.all() | ||
Line 284: | Line 281: | ||
====CakePHP==== | ====CakePHP==== | ||
Using convention over configuration in its minimalist expression CakePHP is a winner. | Using convention over configuration in its minimalist expression CakePHP is a winner. Code is short and completely readable. The variable $posts is made available to the view defined in /recipes/index.ctp | ||
<pre> | <pre> | ||
Line 293: | Line 290: | ||
====Catalyst==== | ====Catalyst==== | ||
I must confess that I'm being rough with Catalyst. I left the comments because the code is unreadable and the phrase “glue together” describes exactly what I feel for this framework. | I must confess that I'm being rough with Catalyst. I left the comments because the code is unreadable and the phrase “glue together” (code from official tutorial) describes exactly what I feel for this framework. The actual code's length is pretty short. | ||
</pre> | </pre> | ||
=head2 list | |||
Fetch all recipes and pass to recipes/list.tt2 in stash to be shown | |||
=cut | |||
sub list :Local { | |||
# Retrieve the usual Perl OO '$self' for this object. $c is the | |||
# Catalyst 'Context' that's used to 'glue together' the various | |||
# components that make up the application | |||
my ($self, $c) = @_; | |||
# Retrieve all of the recipe records as recipe model objects and | |||
# store in the stash where they can be accessed by the TT template | |||
$c->stash(recipes => [$c->model('DB::Recipe')->all]); | |||
# Set the TT template to use. | |||
$c->stash(template => 'recipes/list.tt2'); | |||
} | |||
</pre> | </pre> | ||
Line 324: | Line 321: | ||
<h1>Listing Recipes</h1> | <h1>Listing Recipes</h1> | ||
<table> | <table> | ||
<tr> | |||
<th>Category</th> | |||
<th>Title</th> | |||
<th>Description</th> | |||
<th>Instructions</th> | |||
</tr> | |||
<% @recipes.each do |recipe| %> | <% @recipes.each do |recipe| %> | ||
<tr> | |||
<td><%= link_to recipe.category.name, categories_path(recipe.category) %></td> | |||
<td><%= recipe.title %></td> | |||
<td><%= recipe.description %></td> | |||
<td><%= recipe.instructions %></td> | |||
</tr> | |||
<% end %> | <% end %> | ||
</table> | </table> | ||
Line 347: | Line 344: | ||
{% block content %} | {% block content %} | ||
<table> | <table> | ||
<tr> | |||
<th>Category</th> | |||
<th>Title</th> | |||
<th>Description</th> | |||
<th>Instructions</th> | |||
</tr> | |||
{% for recipe in recipe_list %} | {% for recipe in recipe_list %} | ||
<tr> | |||
<td>{{ recipe.title }}</td> | |||
<td><a href="/cookbook/category/{{ recipe.category.id }}/">{{ recipe.category.name }}</a></td> | |||
<td>{{ recipe.description }}</td> | |||
<td>{{ recipe.instructions }}</td> | |||
</tr> | |||
{% endfor %} | {% endfor %} | ||
</table> | </table> | ||
Line 369: | Line 366: | ||
<h1>Listing Recipes</h1> | <h1>Listing Recipes</h1> | ||
<table> | <table> | ||
<tr> | |||
<th>Category</th> | |||
<th>Title</th> | |||
<th>Description</th> | |||
<th>Instructions</th> | |||
</tr> | |||
<?php foreach ($recipes as $recipe): ?> | <?php foreach ($recipes as $recipe): ?> | ||
<tr> | |||
<td><?php echo $this->Html->link($recipe['Category']['name'], array('controller' => 'categories', 'action' => 'view', $recipe['Category']['id'])); ?></td> | |||
<td><?php echo $recipe['Recipe']['title']; ?></td> | |||
<td><?php echo $recipe['Recipe']['description']; ?></td> | |||
<td><?php echo $recipe['Recipe']['instructions']; ?></td> | |||
</tr> | |||
<?php endforeach; ?> | <?php endforeach; ?> | ||
</table> | </table> | ||
Line 390: | Line 387: | ||
[% META title = 'Listing Recipes' -%] | [% META title = 'Listing Recipes' -%] | ||
<table> | <table> | ||
<tr> | |||
<th>Category</th> | |||
<th>Title</th> | |||
<th>Description</th> | |||
<th>Instructions</th> | |||
</tr> | |||
[% FOREACH recipe IN recipes -%] | [% FOREACH recipe IN recipes -%] | ||
<tr> | |||
<td>[% recipe.title %]</td> | |||
<td><a href="[% uri_for('/categories/')_ recipe.category_id %]">[% recipe.category_name %] | |||
</a></td> | </a></td> | ||
<td>[% recipe.description %]</td> | |||
<td>[% recipe.instructions %]</td> | |||
</tr> | |||
[% END -%] | [% END -%] | ||
</table> | </table> | ||
Line 409: | Line 406: | ||
==References== | ==References== | ||
[1] Princeton University. (2010, October) WordNet. [Online]. http://wordnetweb.princeton.edu/perl/webwn?s=framework | [[#References|[1]]] Princeton University. (2010, October) WordNet. [Online]. http://wordnetweb.princeton.edu/perl/webwn?s=framework | ||
[2] Wikipedia. (2010, October) Wikipedia - Web application framework. [Online]. http://en.wikipedia.org/wiki/Web_application_framework | [[#References|[2]]] Wikipedia. (2010, October) Wikipedia - Web application framework. [Online]. http://en.wikipedia.org/wiki/Web_application_framework | ||
[3] Rails Core Team. (2010, October) Rails Wiki. [Online]. http://wiki.rubyonrails.org/ | [[#References|[3]]] Rails Core Team. (2010, October) Rails Wiki. [Online]. http://wiki.rubyonrails.org/ | ||
[4] CakePHP Software Foundation. (2010, August) CakePHP. [Online]. http://cakephp.org | [[#References|[4]]] CakePHP Software Foundation. (2010, August) CakePHP. [Online]. http://cakephp.org | ||
[5] Django Software Foundation. (2010, October) Django. [Online]. http://www.djangoproject.com | [[#References|[5]]] Django Software Foundation. (2010, October) Django. [Online]. http://www.djangoproject.com | ||
[6] (2010, October) Catalyst Web Framework. [Online]. http://www.catalystframework.org/ | [[#References|[6]]] (2010, October) Catalyst Web Framework. [Online]. http://www.catalystframework.org/ | ||
[7] CakePHP Software Foundation. (2010, October) The Cookbook. [Online]. http://book.cakephp.org/ | [[#References|[7]]] CakePHP Software Foundation. (2010, October) The Cookbook. [Online]. http://book.cakephp.org/ | ||
[8] Cake Software Foundation. (2010, October) The Bakery. [Online]. http://bakery.cakephp.org/ | [[#References|[8]]] Cake Software Foundation. (2010, October) The Bakery. [Online]. http://bakery.cakephp.org/ | ||
[9] (2010, October) Catalyst Manual. [Online]. http://search.cpan.org/~hkclark/Catalyst-Manual-5.8004/lib/Catalyst/Manual/Tutorial.pod | [[#References|[9]]] (2010, October) Catalyst Manual. [Online]. http://search.cpan.org/~hkclark/Catalyst-Manual-5.8004/lib/Catalyst/Manual/Tutorial.pod |
Latest revision as of 22:53, 6 October 2010
Web Application Frameworks
Introduction
What's a web application framework? What is a framework? A framework is an underlying structure [1], and a web application framework is just that. Commonly web application frameworks are associated with agile/fast development; this characteristic is attached to the Model-View-Controller (MVC) architectural pattern. MVC is, by far, the most popular pattern used in web application frameworks. According to Wikipedia [2] content management systems (CMS) are also considered web application frameworks, but we will leave those aside.
Before web application frameworks, websites that dealt with dynamic content were often written from scratch and a page at a time, each page was responsible for handling its own requests. Websites were rigid and code was repeated everywhere (SQL operations, parameter handling, etc.). One step backwards, websites didn't include dynamic content at all and were written entirely in static HTML files, leaving the responsibility of updating the site to developers.
In this chapter we will study four web application frameworks. This chapter was written in a “new web application frameworks for Ruby on Rails developers” way, showing Rails' way first and comparing the rest of the web frameworks with it. However its knowledge is not necessary to comprehend the topics being discussed.
The first section will present the frameworks being examined, a brief description and a list of key points they show at their websites. Immediately after we'll examine the models, view and controllers of the Cookbook application written in all four web frameworks.
Another Gang of Four… Not Really! Four Web Application Frameworks
I guess they could hang together and have fun, but they usually don't. This is another war, just like the one browsers, operating systems and, more recently, mobile operating systems fight. You either love it, or hate it, but we'll try to give each some love.
Ruby on Rails
Website | rubyonrails.org |
Latest Version | 3.0 |
Developers | Rails Core Team |
Slogan | Web development that doesn't hurt |
Logo | Logo can be found in http://commons.wikimedia.org/wiki/File:Ruby_on_Rails_logo.jpg |
Ruby on Rails is the oldest of the four web applications frameworks presented; it was initially released in 2004 [3], the rest were released anytime in 2005 [4], [5], [6]. It's so popular that CakePHP was admittedly inspired on it ([7]) and many other web frameworks were also inspired by its success. Rails openly follow the convention over configuration (COC) paradigm as one of its main tenets [3]. Other languages don't mention this, but as far as I'm concerned they all follow this paradigm; it's the COC paradigm that allows writing entire applications in few lines of code.
Key points
Listed as the “basic tenets of Ruby on Rails” [3].
- Convention over Configuration
- Don't Repeat Yourself (DRY)
- Fat Model, Skinny Controller
- REST Interface
- Write Good Test
- Automate tasks
Django
Website | www.djangoproject.com |
Latest Version | 1.2.3 |
Owners and Distributors | Django Software Foundation |
Slogan | The Web framework for perfectionists with deadlines |
Logo |
Django is a “high-level [5]” web application framework written in Python. It follows the MVC architectural pattern and focuses on automating and adhering to the DRY principle. It was conceived in an online-news operation to handle two challenges: intensive deadlines and “stringent requirements of the experienced Web Developers who wrote it. [5]”
One of the big things in Django is its “automatic admin interface.” Other frameworks can achieve this by including third party components/plugins, but the fact that Django includes it by default is a plus. Authentication and authorization, using access control list (ACL), are added to your application, almost without intervention from the developer.
Key points
Listed in an untitled list on their website [5].
- Object-relational mapper (ORM)
- Automatic admin interface
- Elegant URL design
- Template System
- Cache System
- Internationalization
CakePHP
Website | cakephp.org |
Latest Version | 1.3.4 |
Releaser | CakePHP Software Foundation, Inc |
Logo |
CakePHP is a “rapid [4]” development framework written in PHP. It uses the design patterns MVC and ORM. As stated before, CakePHP was inspired on the success of Ruby on Rails [7]. One thing not mentioned on the “hot features” is that CakePHP supports dynamic scaffolding [7]. Rails originally included this feature, but as for Rails 2.0 dynamic scaffolding is no longer supported [3]. Dynamic scaffolding allows the user to test its Create, Read, Updated, and Delete (CRUD) functions by adding one line of code in their controller:
var $scaffold;
Its importance relies on allowing the user to test relations and validations on the models before generating the code on the controller (static scaffolding) and that the code generated will take into consideration the changes made to the models during the dynamic scaffolding phase.
Key points
Listed as “Hot Features” on [4].
- No configuration
- Extremely Simple
- Active, Friendly Community
- Flexible License
- Clean IP
- Best Practices
- Object Oriented
Catalyst
Website | www.catalystframework.org |
Latest Version | 5.80029 |
Slogan | The elegant MVC framework |
Logo | Logo can be found in http://commons.wikimedia.org/wiki/File:Catalyst_logo3.png |
Catalyst is a web application framework written in PERL. It was done with the DRY principle in mind and reuses many PERL modules that performed well on the past.
Key points
Or “What makes Catalyst special?” as they like to call them [6].
- Keep it Simple, Stupid
- Don't Repeat Yourself
- There is More Than One Way To Do It
- Plugins
- A self-restarting development server (it self-restarts when source code is changed)
Model-View-Controller
In this section we'll examine each web application framework from a model, view, and controller perspective. Excerpts of the “Cookbook” application will be shown for each element on each framework. To keep a better flow of the lecture controllers will be presented before views, since in all frameworks variables needed by views are set there. We'll discuss code readability, briefness and if they do what they are supposed to do.
Models
The model “Recipe” is shown, with its association to “Category” and a few validations. All frameworks try to keep the model fat and a slim controller, by adding the validations on the model.
Ruby on Rails
Rails does a good job making its code readable, apart from the to_s method. Defaults errors are shown if a validation fails and a dynamic variable is created to hold is parent Category.
This model has no intervention on defining the table on the database. To be fair with Django, additional to this class, the CreateRecipes class (db/migrate) is shown. However that code was written almost entirely automatically by the generator so one could argue that it's not necessarily a fair comparison. The command “rake db:migrate” creates the database on the table based on the contents of the db/migrate folder.
class Recipe < ActiveRecord::Base belongs_to :category validates_length_of :title, :maximum => 255 validates_length_of :description, :maximum => 255 def to_s self.login end end class CreateRecipes < ActiveRecord::Migration def self.up create_table :recipes do |t| t.string :title, :limit => 255 t.string :description, :limit => 255 t.text :instructions t.integer :category_id t.timestamps end end def self.down drop_table :recipes end end
Django
Being a neophyte in Python this code looked strange at first sight. While writing the code I had as many indentation errors as semicolons I tried to place in Ruby when I first learned it. The code is not as readable as in Rails, but it's short. Running the command “python manage.py syncdb” synchronizes the database structure with the contents of this file. Related objects and validations are also created. All models are defined in a single file called models.py.
class Recipe(models.Model): title = models.CharField(max_length=255) description = models.CharField(max_length=255) instructions = models.TextField() category = models.ForeignKey(Category) created = models.DateField(auto_now_add=True) updated = models.DateField(auto_now=True) def __unicode__(self): return self.title
CakePHP
CakePHP might look a lot more like Java or other widely known static languages (it's still a dynamically typed language, I'm comparing just in the syntax), just add “$” to each variable an treat arrays as both an Array and a HashTable. When putting the code side by side with Django and Ruby on Rails it looks verbosely. On top of that, there is no built-in database migration to keep a definition of the database inside the project. This is compensated by many components available at the Bakery [8].
<?php class Recipe extends AppModel { var $name = 'Recipe'; var $belongsTo = 'Category'; var $displayField = 'title'; var $validate = array( 'title' => array( 'rule' => 'notEmpty', 'rule' => array('maxLength', 255), ), 'description' => array( 'rule' => 'notEmpty', 'rule' => array('maxLength', 255), ) ); } ?>
Catalyst
The tables need to be created directly on the database before generating the model. This model is generated by running the following script [9].
script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ create=static dbi:SQLite:myapp.db \ on_connect_do="PRAGMA foreign_keys = ON"
The script reads the database and generates the following code. Neither the script, nor the code is readable. It verboseness is extravagant, which I think is a result of PERL's age.
use strict; use warnings; use base 'DBIx::Class'; __PACKAGE__->load_components("InflateColumn::DateTime", "Core"); __PACKAGE__->table("recipes"); __PACKAGE__->add_columns( "id", { data_type => "INTEGER", is_nullable => 0, size => undef }, "title", { data_type => "VARCHAR", is_nullable => 0, size => 255 }, "description", { data_type => "VARCHAR", is_nullable => 0, size => 255 }, "instructions", { data_type => "text", is_nullable => 0 }, "created_at", { data_type => "DATETIME", is_nullable => 0 }, "updated_at", { data_type => "DATETIME", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to( ); 1;
Controllers
To keep it short, we'll show just one method of the controller: index/list, which list all recipe objects and sends that information to the corresponding view.
Ruby on Rails
Rails does a good job keeping the controller slim. The convention over configuration is also shown here were format.html renders the index.html.erb file by default. The method responds to the GET action under /recipes because of pre-define routes created when scaffolding.
# GET /recipes def index @recipes = Recipe.all respond_to do |format| format.html end end
Django
Just because Rails did a good job doesn't mean that Django wouldn't. They actually have a package named “shortcuts” which holds many DRY-oriented functions that perform common tasks. Something unique in Django is that this functional code is actually defined in the views.py file. And what we know as views is defined in templates. Just as in models, all controller functions are defined in a single file, keeping the file count small.
from django.shortcuts import render_to_response, … …
def index(request): recipes = Recipe.objects.all() return render_to_response('cookbook/recipe.html', {'recipes': recipes})
CakePHP
Using convention over configuration in its minimalist expression CakePHP is a winner. Code is short and completely readable. The variable $posts is made available to the view defined in /recipes/index.ctp
function index() { $this->set('recipes', $this->Recipe->find('all')); }
Catalyst
I must confess that I'm being rough with Catalyst. I left the comments because the code is unreadable and the phrase “glue together” (code from official tutorial) describes exactly what I feel for this framework. The actual code's length is pretty short.
=head2 list Fetch all recipes and pass to recipes/list.tt2 in stash to be shown =cut
sub list :Local { # Retrieve the usual Perl OO '$self' for this object. $c is the # Catalyst 'Context' that's used to 'glue together' the various # components that make up the application my ($self, $c) = @_;
# Retrieve all of the recipe records as recipe model objects and # store in the stash where they can be accessed by the TT template $c->stash(recipes => [$c->model('DB::Recipe')->all]);
# Set the TT template to use. $c->stash(template => 'recipes/list.tt2'); }
Views
All views/templates are very similar. They all have a hierarchy using different keywords: extends, render, include, etc., but at the end they all serve a similar purpose.
Of all CakePHP had the must verbosely way of echoing (print in PHP terms) the values of properties inside the object. Django differs from the rest in the place they store their templates, as they call them. This is usually a folder outside of the project specified in the settings.py file.
Ruby on Rails
<h1>Listing Recipes</h1> <table> <tr> <th>Category</th> <th>Title</th> <th>Description</th> <th>Instructions</th> </tr> <% @recipes.each do |recipe| %> <tr> <td><%= link_to recipe.category.name, categories_path(recipe.category) %></td> <td><%= recipe.title %></td> <td><%= recipe.description %></td> <td><%= recipe.instructions %></td> </tr> <% end %> </table>
Django
{% extends "base_generic.html" %} {% block title %}Listing Recipes{% endblock %} {% block content %} <table> <tr> <th>Category</th> <th>Title</th> <th>Description</th> <th>Instructions</th> </tr> {% for recipe in recipe_list %} <tr> <td>{{ recipe.title }}</td> <td><a href="/cookbook/category/{{ recipe.category.id }}/">{{ recipe.category.name }}</a></td> <td>{{ recipe.description }}</td> <td>{{ recipe.instructions }}</td> </tr> {% endfor %} </table> {% endblock %}
CakePHP
<h1>Listing Recipes</h1> <table> <tr> <th>Category</th> <th>Title</th> <th>Description</th> <th>Instructions</th> </tr> <?php foreach ($recipes as $recipe): ?> <tr> <td><?php echo $this->Html->link($recipe['Category']['name'], array('controller' => 'categories', 'action' => 'view', $recipe['Category']['id'])); ?></td> <td><?php echo $recipe['Recipe']['title']; ?></td> <td><?php echo $recipe['Recipe']['description']; ?></td> <td><?php echo $recipe['Recipe']['instructions']; ?></td> </tr> <?php endforeach; ?> </table>
Catalyst
[% META title = 'Listing Recipes' -%] <table> <tr> <th>Category</th> <th>Title</th> <th>Description</th> <th>Instructions</th> </tr> [% FOREACH recipe IN recipes -%] <tr> <td>[% recipe.title %]</td> <td><a href="[% uri_for('/categories/')_ recipe.category_id %]">[% recipe.category_name %] </a></td> <td>[% recipe.description %]</td> <td>[% recipe.instructions %]</td> </tr> [% END -%] </table>
References
[1] Princeton University. (2010, October) WordNet. [Online]. http://wordnetweb.princeton.edu/perl/webwn?s=framework
[2] Wikipedia. (2010, October) Wikipedia - Web application framework. [Online]. http://en.wikipedia.org/wiki/Web_application_framework
[3] Rails Core Team. (2010, October) Rails Wiki. [Online]. http://wiki.rubyonrails.org/
[4] CakePHP Software Foundation. (2010, August) CakePHP. [Online]. http://cakephp.org
[5] Django Software Foundation. (2010, October) Django. [Online]. http://www.djangoproject.com
[6] (2010, October) Catalyst Web Framework. [Online]. http://www.catalystframework.org/
[7] CakePHP Software Foundation. (2010, October) The Cookbook. [Online]. http://book.cakephp.org/
[8] Cake Software Foundation. (2010, October) The Bakery. [Online]. http://bakery.cakephp.org/
[9] (2010, October) Catalyst Manual. [Online]. http://search.cpan.org/~hkclark/Catalyst-Manual-5.8004/lib/Catalyst/Manual/Tutorial.pod