<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christopher H. Laco &#187; cakephp</title>
	<atom:link href="http://chrislaco.com/tag/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://chrislaco.com</link>
	<description></description>
	<lastBuildDate>Wed, 28 Jul 2010 00:21:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>MVC Marathon Part 3: Creating a Restaurants Controller and View</title>
		<link>http://chrislaco.com/articles/mvc-marathon-part-3-creating-a-restaurants-controller-and-view/</link>
		<comments>http://chrislaco.com/articles/mvc-marathon-part-3-creating-a-restaurants-controller-and-view/#comments</comments>
		<pubDate>Sun, 10 Aug 2008 20:15:23 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[catalyst]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[merb]]></category>
		<category><![CDATA[mojo]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=7</guid>
		<description><![CDATA[Welcome to part 3 of MVC Marathon, a multipart excursion into creating an application in the major MVC frameworks available today. The source code for this part can be found here: http://github.com/claco/mvc-marathon/tree/part3/ Part 3: Creating a Restaurants Controller and View &#8230; <a href="http://chrislaco.com/articles/mvc-marathon-part-3-creating-a-restaurants-controller-and-view/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Welcome to part 3 of <a href="/articles/mvc-madness/">MVC Marathon</a>, a multipart excursion into creating an application in the major MVC frameworks available today.</p>
<p>The source code for this part can be found here:<a href="http://github.com/claco/mvc-marathon/tree/part3/"> http://github.com/claco/mvc-marathon/tree/part3/</a></p>
<p><span id="more-7"></span></p>
<h2>Part 3: Creating a Restaurants Controller and View</h2>
<p>In our last installment, we created a database, schema and model to get at our list of restaurants. In this episode, I&#8217;m going to create a controller and view that uses that model to list the restaurants in our database.  Where available, I will also use any existing CRUD/scaffolding to enter some test data.</p>
<p>You can jump to any specific framework using the links below.</p>
<ul>
<li><a href="#aspnetmvc">ASP.NET MVC</a></li>
<li><a href="#cakephp">CakePHP</a></li>
<li><a href="#catalyst">Catalyst</a></li>
<li><a href="#django">Django</a></li>
<li><a href="#rails">Ruby on Rails</a></li>
<li><a href="#conclusions">Conclusions</a></li>
</ul>
<h2><a name="aspnetmvc"></a>ASP.NET MVC</h2>
<p>While .NET 3.0 and MVC have made a lot of strides with LINQ and Entity Framework (ORM), they completely lack any kind of scaffolding or helpers for data management. I&#8217;m sure someone is working on it, and there are 3rd party package available. For now, we&#8217;ll stick to just creating a controller for the restaurants.</p>
<p><strong>Update: I dropped the ball on this one. There is the new Dynamic Data scaffolding in .NET 3.5. It appears that you can create an entirely separate Dynamic Data site, or <a href="http://msdn.microsoft.com/en-us/library/cc668188.aspx">try and work it into an existing site</a>. As it stands,  a separate site doesn&#8217;t count as part of the MVC solution, and the steps to integrate it are woefully long and complicated. I declare that the same as no real new user friendly solution for the context of this series.</strong></p>
<h3>Creating the Data</h3>
<p>Before we create our controller, we need to add some test data. First, Double-Click the BurningPlate.mdf file in the App_Data directory to open the Server Explorer pane. Right-Click on the Restaurants table and select &#8220;Show Table Data&#8221;.</p>
<p><a href="aspnet1.png"><br />
<img src="aspnet1.png" alt="create data" width="204" height="152" /><br />
</a></p>
<p>Now enter two restaurants into the Name field: &#8220;Siamone Thai&#8221; and &#8220;Azteca&#8221;.</p>
<h3>Creating the Controller</h3>
<p>Now that we have our test data, we&#8217;re going to create a controller to list that data. Right-Click on the Controllers folder and choose &#8220;Add New Item&#8221;.</p>
<p><a href="aspnet2.png"><br />
<img src="aspnet2.png" alt="create controller" width="204" height="152" /><br />
</a></p>
<p>Select &#8220;MVC Controller Class&#8221;, type RestaurantsController.cs for the name and click &#8220;Add&#8221;. By convention in .NET MVC, the controller class name should always end in Controller. The default routing will assume that any controller path named /foo/ will map to the controller FooController. This of course can be changed by tweaking the routing, but why create more work for yourself?</p>
<p>Much like inflection to relate models to tables automatically, controller and view naming is typically referred to as &#8220;convention or configuration&#8221;. You can follow naming conventions and have things &#8220;Just Work&#8221;, or you can usually deviate from the norm and use configuration to over come situations where you need t name things differently.</p>
<p><em>Update: The file name in the add item dialog above is wrong. <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </em></p>
<p><a href="aspnet3.png"><br />
<img src="aspnet3.png" alt="new controller" width="204" height="152" /><br />
</a></p>
<p>Now we have a shiny new controller class that by default spits out a NotImplementedException. Let&#8217;s add some code to get a list of restaurants and feed that to a view.</p>
<p><a href="aspnet4.png"><br />
<img src="aspnet4.png" alt="new controller code" width="204" height="152" /><br />
</a></p>
<pre class="brush:csharp">public ActionResult Index()
{
  BurningPlateDataContext db = new BurningPlateDataContext();
  List&lt;Restaurant&gt; restaurants = db.Restaurants.ToList();

  return View(restaurants);
}</pre>
<p>In the code above, all we&#8217;ve had to do is instantiate an instance of the BurningPlateDataContext class that was auto generated when we <a href="/articles/mvc-marathon-part-2-creating-a-database-and-model/#aspnetmvc">created our Model</a>. Then we asked it for a list of restaurants using ToList. Finally, we passed that list of restaurants to the View method.</p>
<p>Explore the View method when you have time. It has many options, and there is more than one way to get data into a view. In our case we&#8217;ve chosen to pass the restaurants list using the Model parameter. You can also pass it in the ViewData itself. I personally think that the main model data should be passed in the Model, and ViewData is reserved for all of the extra information like page titles, meta tag keywords, links, etc.</p>
<h3>Creating the View</h3>
<p>Now that we have a controller that assembles the data, we need a way to display it. My much like the routes to controller mappings, .NET MVC will look for a view matching the ControllerName/Action path. Since our controller is Restaurants and our action is Index, we need to create an Index view in the Restaurants folder.</p>
<p>Just like the routing naming conventions, this can also be customized to use views with alternate names and in alternate locations using the various View method options.</p>
<p>To create the view, Right-Click the Views folder and create a folder called &#8220;Restaurants&#8221;. Right-Click that new folder and select &#8220;Add Item&#8221;.</p>
<p><a href="aspnet5.png"><br />
<img src="aspnet5.png" alt="new view" width="204" height="152" /><br />
</a></p>
<p>Because .NET MVC supports master pages (wrapper templated layouts) by default, select &#8220;MVC View Content Page&#8221; and name it after our default RestaurantController method &#8220;Index&#8221; and click &#8220;Add&#8221;. You will then be prompted to select a master page. Select the &#8220;Site&#8221; master page in the Shared folder and click &#8220;OK&#8221;.</p>
<p><a href="aspnet6.png"><br />
<img src="aspnet6.png" alt="new view master select" width="204" height="152" /><br />
</a></p>
<p>Now that we have a new view page, we need to tell it about the type of model data we&#8217;re passing it from the controllers View method call in the controller. In our case, we&#8217;re passing a list o restaurants. Right-Click on your new Index.aspx page and select &#8220;View Code&#8221;.</p>
<p><a href="aspnet7.png"><br />
<img src="aspnet7.png" alt="new view model type" width="204" height="152" /><br />
</a></p>
<pre class="brush:csharp">public partial class Index : ViewPage&lt;List&lt;Restaurant&gt;&gt;</pre>
<p>To tell the view to expect a list of restaurants, we simply add a type definition to the view class definition. In our case, that means changing ViewPage to ViewPage&lt;List&lt;Restaurants&gt;&gt;. This tells intellisense that when accessing the ViewData.Model property that it is a List of Restaurants objects.</p>
<p>Now that we have told the page what the model is, we simply need to add a loop to the template to display the list of restaurants. Double-Click your Index.aspx file to open the page view.</p>
<p><a href="aspnet8.png"><br />
<img src="aspnet8.png" alt="new view model loop" width="204" height="152" /><br />
</a></p>
<pre class="brush:csharp">&lt;ul&gt;
    &lt;% foreach (var restaurant in ViewData.Model) { %&gt;
        &lt;li&gt;&lt;%= restaurant.Name %&gt;&lt;/li&gt;
    &lt;% } %&gt;
&lt;/ul&gt;</pre>
<p>In our view, we added a loop against the ViewData.Model and printed out each restaurants name in an unordered list.</p>
<p>Now that we have a controller and a view, let&#8217;s check out our work by going to /restaurants/.</p>
<p><a href="aspnet11.png"><br />
<img src="aspnet11.png" alt="new view model browser" width="204" height="152" /><br />
</a></p>
<p>As a final touch, let&#8217;s add a link to the home page to get to our new restaurants index view. To do that, open the Site.Master view and between the Home and About Us links, add a call to Html.ActionLink to build our link.</p>
<p><a href="aspnet9.png"><br />
<img src="aspnet9.png" alt="new view model browser" width="204" height="152" /><br />
</a></p>
<pre class="brush:csharp">&lt;li&gt;
  &lt;%= Html.ActionLink("Restaurants", "Index", "Restaurants") %&gt;
&lt;/li&gt;</pre>
<p>Hardcoding links suck. MVC takes care of this by using the routing tables to create a link using the controller and action names. This means that if the controller or action names change, the links to then automatically change as well. No need to go rooting through code to update links.</p>
<p>Let&#8217;s check out our link to make sure it works.</p>
<p><a href="aspnet10.png"><br />
<img src="aspnet10.png" alt="new view model browser" width="204" height="152" /><br />
</a></p>
<h2><a name="cakephp"></a>CakePHP</h2>
<p>Unlike .NET MVC, CakePHP has CRUD scaffolding already built into<br />
the framework. Because of this, we&#8217;re going to create our<br />
controller first and then create our data.</p>
<h3>Creating the Controller</h3>
<p>Just like when we created our model, we&#8217;ll use<br />
<kbd>cake bake</kbd> to create the controller for us.</p>
<p>[code lang="bash"]claco@mbp ~/mvc-marathon/cakephp/BurningPlate $ cake bake</p>
<p>Welcome to CakePHP v1.2.0.7296 RC2 Console<br />
---------------------------------------------------------------<br />
App : BurningPlate<br />
Path: /Users/claco/mvc-marathon/cakephp/BurningPlate<br />
---------------------------------------------------------------<br />
Interactive Bake Shell<br />
---------------------------------------------------------------<br />
[D]atabase Configuration<br />
[M]odel<br />
[V]iew<br />
[C]ontroller<br />
[P]roject<br />
[Q]uit<br />
What would you like to Bake? (D/M/V/C/P/Q)<br />
> C<br />
---------------------------------------------------------------<br />
Bake Controller<br />
Path: /Users/claco/mvc-marathon/cakephp/BurningPlate/controllers/<br />
---------------------------------------------------------------<br />
Possible Controllers based on your current database:<br />
1. Restaurants<br />
Enter a number from the list above, type in the name of another controller, or 'q' to exit<br />
[q] > 1<br />
---------------------------------------------------------------<br />
Baking RestaurantsController<br />
---------------------------------------------------------------<br />
Would you like to build your controller interactively? (y/n)<br />
[y] ><br />
Would you like to use scaffolding? (y/n)<br />
[n] > y</p>
<p>---------------------------------------------------------------<br />
The following controller will be created:<br />
---------------------------------------------------------------<br />
Controller Name:  Restaurants<br />
var $scaffold;<br />
---------------------------------------------------------------<br />
Look okay? (y/n)<br />
[y] ></p>
<p>Creating file /Users/claco/mvc-marathon/cakephp/BurningPlate/controllers/restaurants_controller.php<br />
Wrote /Users/claco/mvc-marathon/cakephp/BurningPlate/controllers/restaurants_controller.php<br />
Cake test suite not installed.  Do you want to bake unit test files anyway? (y/n)<br />
[y] ></p>
<p>You can download the Cake test suite from http://cakeforge.org/projects/testsuite/</p>
<p>Baking unit test for Restaurants...</p>
<p>Creating file /Users/claco/mvc-marathon/cakephp/BurningPlate/tests/cases/controllers/restaurants_controller.test.php<br />
Wrote /Users/claco/mvc-marathon/cakephp/BurningPlate/tests/cases/controllers/restaurants_controller.test.php<br />
---------------------------------------------------------------<br />
Interactive Bake Shell<br />
---------------------------------------------------------------<br />
[D]atabase Configuration<br />
[M]odel<br />
[V]iew<br />
[C]ontroller<br />
[P]roject<br />
[Q]uit<br />
What would you like to Bake? (D/M/V/C/P/Q)<br />
> Q[/code]</p>
<p>Here we&#8217;ve asked to create a new controller. Cake knew what models<br />
and tables we have and prompts us to choose one. It also asked if<br />
we want to use scaffolding and create unit tests.</p>
<p>[code lang="php"]<?php<br />
class RestaurantsController extends AppController {</p>
<p>var $name = 'Restaurants';<br />
var $scaffold;<br />
}<br />
?>[/code]</p>
<p>As we see form the controller that was created, the only code<br />
needed to CRUD data is <samp>var $scaffold;</samp>. Also, just<br />
like .NET MVC, the class is named RestaurantsController, which<br />
routing will map to the /restaurants/ path.</p>
<h3>Creating the View</h3>
<p>Because we&#8217;ve chosen to use scaffolding, the views and all of the<br />
necessary code needed to maintait the restaurants table is already<br />
taken care of. Let&#8217;s take a look at what it provides:</p>
<p><a href="cake1.png"><br />
<img src="cake1.png" alt="cake list" width="204" height="152" /><br />
</a><br />
<a href="cake2.png"><br />
<img src="cake2.png" alt="cake new" width="204" height="152" /><br />
</a><br />
<a href="cake3.png"><br />
<img src="cake3.png" alt="cake list" width="204" height="152" /><br />
</a></p>
<p>With one variable, we have a full working scaffolding to manage</p>
<p>our data. Now, let&#8217;s add a link to the front page going to our<br />
new restaurants controller.</p>
<p>To add our link we need to edit the home.ctp page in the Views<br />
f√•older.</p>
<pre>&lt;a href="&lt;?php echo Router::url(array('controller' =&gt; 'Restaurants', 'action' =&gt; 'index'));?&gt;"&gt;Restaurants&lt;/a&gt;</pre>
<p>Just like .NET MVC, we&#8217;re using the routing information to<br />
generate a link rather than hard coding it, passing in the<br />
controller name and the action we&#8217;re linking to. Now that should<br />
appear on the default home page.</p>
<p><a href="cake4.png"><br />
<img src="cake4.png" alt="new view model browser" width="204" height="152" /><br />
</a></p>
<h2><a name="catalyst"></a>Catalyst</h2>
<p>Because Catalyst doesn&#8217;t force any specific ORM upon you, that<br />
also means that there is no official CRUD or scaffolding support<br />
out of the box. Depending on the ORM you choose, there are some<br />
pretty good options.</p>
<p>Class::DBI users have Catalyst::Enzyme.<br />
Rose::DB users have Rose::DBx::Garden::Catalyst<br />
(based on CatalystX::CRUD). While there are CatalystX::CRUD Models<br />
, you still have to write forms views and processing for it.<br />
There is also Catalyst-Example-InstantCRUD seems to be old and dead.<br />
There&#8217;s also CatalystX::ListFramework and CatalystX::ListFramework::Builder<br />
<span style="text-decoration: line-through;">which requires some wireup work for the former and the latter<br />
creates an entire application, apparently separate from the public application.</span></p>
<p style="text-decoration: line-through;">Since we&#8217;re using DBIx::Class, and the current options don&#8217;t meet<br />
our needs without a divergence from this series, we&#8217;ll skip CRUD<br />
scaffolding for the time being.</p>
<h3>Creating the Data</h3>
<p style="text-decoration: line-through;">Since we have no CRUD scaffolding, we need to create the data the<br />
old fashioned way. You have to choices. First, use the dbicadmin script<br />
to run insert statements. Second, use the sqlite command line<br />
utilities to run SQL insert statement. I chose the latter. I won&#8217;t<br />
cover that utility usage here.</p>
<p>To add the CRUD scaffolding to our add, we need to load the CatalystX::ListFramework::Builder plugin, point it to our extjs javascript files and tell it to live in the admin url space.</p>
<p>To load the plugin, simple add it to the use Catalyst line in your application file:</p>
<pre># Start the application
__PACKAGE__-&gt;setup(qw/
-Debug
ConfigLoader
+CatalystX::ListFramework::Builder
Static::Simple
/);</pre>
<p>Now we need to tell it where our ExtJS files are located. We do this by adding enries to our configuration file for the plugins root controller. This is covered in its documentation. While we&#8217;re in the config file, we can also tell it to put the CRUD in the admin url namespace:</p>
<pre>extjs2   /static/extjs
&lt;Controller::LFB::Root&gt;
&lt;action&gt;
&lt;base&gt;
PathPart admin
&lt;/base&gt;
&lt;/action&gt;
&lt;/Controller::LFB::Root&gt;</pre>
<p style="text-decoration: line-through;">Currently, the CRUD requires it&#8217;s own Model to access the database. I expect this will change in the future. In the mean time, we can just create a model that inherits from the model we&#8217;ve previously created.</p>
<pre style="text-decoration: line-through;">package BurningPlate::Model::LFB::DBIC;

use strict;
use base 'BurningPlate::Model::DB';

1;</pre>
<p>The latest versions of LFB automatically detect any models that use DBIC::Schema and adds them to the main admin index page.</p>
<p>Now fire up your application and go to the admin url you specified in config. From there we can use the interface to add data.</p>
<p><a href="catalyst2.png"><br />
<img src="catalyst2.png" alt="catalyst list" width="204" height="152" /><br />
</a><br />
<a href="catalyst3.png"><br />
<img src="catalyst3.png" alt="catalyst add" width="204" height="152" /><br />
</a><br />
<a href="catalyst4.png"><br />
<img src="catalyst4.png" alt="catalyst add" width="204" height="152" /><br />
</a><br />
<a href="catalyst5.png"><br />
<img src="catalyst5.png" alt="catalyst list" width="204" height="152" /><br />
</a></p>
<h3>Creating the Controller</h3>
<p>To create the controller, we&#8217;ll just call the scripts/*create.pl<br />
script, giving it the name of the controller to create.</p>
<pre>claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*create.pl controller Restaurants
exists "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/Controller"
exists "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/Controller/Restaurants.pm"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t/controller_Restaurants.t"</pre>
<p>The script has created a new controller class for us along with<br />
a unit test file for that controller. Now we need to add code<br />
to retrieve a list of restaurants and send that to a view.</p>
<pre>$c-&gt;stash-&gt;{'restaurants'} = [$c-&gt;model('Restaurant')-&gt;search-&gt;all];</pre>
<p>In the code above, we ask the Restaurant model to search for all<br />
restaurant records and assign the results to an array called<br />
restaurants in the stash. The stash is just like the ViewData in<br />
.NET MVC, holding data for the view to access and render.</p>
<h3>Creating the View</h3>
<p>Now that we have our restaurants, we need a view to display them.<br />
Catalyst doesn&#8217;t assume out of the box that your views will be<br />
in any specific language, or even that they will use templates at all.<br />
This means we need to create a view class to render templates and<br />
an actual template.</p>
<p>We&#8217;ll use the create.pl script again to create a view class.</p>
<pre>claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*create.pl view TT TT
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/View"
exists "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/View/TT.pm"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t/view_TT.t"</pre>
<p>Here&#8217;s we&#8217;ve created a view class using the TT (Template Toolkit)<br />
helper. By default, Catalyst will use the first view it finds as the<br />
default view renderer. If you have multiple views, you can call<br />
the view explicitly or set the default_view or current_view<br />
config options.</p>
<p>Now we need to create a template for our controller. Just like<br />
.NET MVC above, the TT view will look for a template file matching<br />
the controllers name and action. In our case, the file would be<br />
named index.tt in a restaurants folder in the root directory.<br />
You can override the name of the template using the template<br />
stash variable.</p>
<pre>&lt;ul&gt;
[% FOREACH restaurant IN restaurants %]
&lt;li&gt;[% restaurant.name %]&lt;/li&gt;
[% END %]
&lt;/ul&gt;</pre>
<p>Just like the index template for .NET MVC, we simply use the template<br />
language of choice to create an unordered list, loop through each<br />
restaurant and print out the name.</p>
<p>Now that we have our view, let&#8217;s check out our restaurants list<br />
by going to /restaurants/. Just like Cake and .NET, the path is<br />
derived form the name of the class. Unlike Cake and .NET, there<br />
is no central routing configuration to tweak if you need to<br />
change the urls. Instead, you can simply change the path<br />
configuration variable in each controller. You can however change<br />
that config parameter within the main configuration file too,<br />
so I guess there technically is a central routing table. <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><a href="catalyst1.png"><br />
<img src="catalyst1.png" alt="catalyst browser" width="204" height="152" /><br />
</a></p>
<h2><a name="django"></a>Django</h2>
<p>Because Django was born our of the publishing industry, it has<br />
very strong CRUD/scaffolding support out of the box. One needs<br />
only to load the &#8220;admin&#8221; modules in their configuration, tell<br />
it what models to administer, set the admin url and create<br />
the necessary tables and user using manage.py.</p>
<h3>Creating the Data</h3>
<p>In our settings.py, we add a line to load the django.contrib.admin<br />
module:</p>
<pre>INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'BurningPlate'
)</pre>
<p>Now we need to tell the admin code which models to administer. We can<br />
do this anywhere, but I&#8217;ll put it in our model for now.</p>
<pre>admin.site.register(Restaurant)</pre>
<p>Now we need to tell Django what url to use for admin access. To do this,<br />
all we have to do is uncomment some like already in the urls.py file:</p>
<pre>from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
# Example:
# (r'^BurningPlate/', include('BurningPlate.foo.urls')),

# Uncomment the next line to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line for to enable the admin:
(r'^admin/(.*)', admin.site.root),
)</pre>
<p>Now that we loaded the admin code and told it which models to administer,<br />
we need to update the database to include the necessary admin related<br />
tables. To do this, simply run syncdb.</p>
<pre>claco@mbp ~/BurningPlate $ python manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table BurningPlate_restaurant

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'claco'): admin
E-mail address: claco@chrislaco.com
Password:
Password (again):
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model</pre>
<p>Django created the needed tables for us and also prompted us to create<br />
a username/password for access. Now let&#8217;s check out the admin web<br />
interface. and add our data.</p>
<p><a href="django1.png"><br />
<img src="django1.png" alt="django list" width="204" height="152" /><br />
</a><br />
<a href="django2.png"><br />
<img src="django2.png" alt="django new" width="204" height="152" /><br />
</a><br />
<a href="django3.png"><br />
<img src="django3.png" alt="django list" width="204" height="152" /><br />
</a><br />
<a href="django4.png"><br />
<img src="django4.png" alt="django list" width="204" height="152" /><br />
</a></p>
<h3>Creating the Controller</h3>
<p>While Django is an &#8220;MVC&#8221; like framework, it considers the Controller<br />
to be the &#8220;view&#8221;, and the View as the &#8220;template&#8221;. Aside from this<br />
naming juggle, it&#8217;s still the same in practice: a separation of<br />
getting the data and displaying the data.</p>
<p>To create our controller, simply create a restaurants directory<br />
and define the views for it.</p>
<pre>from django.shortcuts import render_to_response
from BurningPlate.models import Restaurant

def index(request):
restaurants = Restaurant.objects.all()
return render_to_response('restaurants/index.html', {'restaurants': restaurants})</pre>
<p>In the code above, we loaded request handling modules, loaded our<br />
Restaurants model and defined an index action that loads all restaurants<br />
and renders the index.html template, passing it the restaurants list in much<br />
the same we use the stash in Catalyst of the ViewData in .NET.<br />
Like Catalyst, the template can be named anything you want it to be.</p>
<p>Unlike the other frameworks, url mapping is a manual process. To<br />
expose the restaurants controller, we need to add it to our urls.py<br />
file:</p>
<pre>(r'^restaurants/$', 'BurningPlate.restaurants.views.index')</pre>
<h3>Creating the View</h3>
<p>By default, Django looks for<br />
its templates in a path outside of the application root.</p>
<p>For the sake of keeping it all in the same directory in the<br />
repository, I&#8217;ve tweaked the config to point to the current<br />
directory as the root template directory.</p>
<pre>TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
"."
)</pre>
<p>Now in our restaurants directory, we create an index.html with<br />
the code to print out the restaurants.</p>
<pre>&lt;ul&gt;
{% for restaurant in restaurants %}
&lt;li&gt;{{ restaurant.name }}&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;</pre>
<p>Looks familiar doesn&#8217;t it? Let&#8217;s check it out in our browser.</p>
<p><a href="django5.png"><br />
<img src="django5.png" alt="django browser" width="204" height="152" /><br />
</a></p>
<h2><a name="rails"></a>Ruby on Rails</h2>
<p>Unfortunately for me, I upgraded to Leopard, which means I also got<br />
the latest Rails 2.1. The scaffolding in 2.1 has changed from<br />
previous versions. It also apparently can&#8217;t be added to an existing<br />
model as it insists on creating it&#8217;s own model as part of the process.<br />
This means we have to ditch the model we built in the last<br />
article.</p>
<h3>Creating the Controller/View/Data</h3>
<p>To create the controller and scaffolding, simply run the generate<br />
scaffold command, passing it the name of the model and the fields<br />
to create:</p>
<pre>claco@mbp ~/mvc-marathon/rails/BurningPlate $ script/generate scaffold restaurant name:string
exists  app/models/
exists  app/controllers/
exists  app/helpers/
create  app/views/restaurants
create  app/views/layouts/
exists  test/functional/
exists  test/unit/
create  public/stylesheets/
create  app/views/restaurants/index.html.erb
create  app/views/restaurants/show.html.erb
create  app/views/restaurants/new.html.erb
create  app/views/restaurants/edit.html.erb
create  app/views/layouts/restaurants.html.erb
create  public/stylesheets/scaffold.css
create  app/controllers/restaurants_controller.rb
create  test/functional/restaurants_controller_test.rb
create  app/helpers/restaurants_helper.rb
route  map.resources :restaurants
dependency  model
exists    app/models/
exists    test/unit/
exists    test/fixtures/
create    app/models/restaurant.rb
create    test/unit/restaurant_test.rb
create    test/fixtures/restaurants.yml
exists    db/migrate
create    db/migrate/20080731023309_create_restaurants.rb</pre>
<p>As you can see, this created everything we need to get started.<br />
The mode, the migration, the views, the controller and the<br />
tests/test fixtures. Normally you would run rake db:migrate to<br />
setup the database. But because we&#8217;ve already done the same in our<br />
previous article, we don&#8217;t need to do that here.</p>
<p>Let&#8217;s check out what we have in the browser.</p>
<p><a href="rails1.png"><br />
<img src="rails1.png" alt="rails list" width="204" height="152" /><br />
</a><br />
<a href="rails2.png"><br />
<img src="rails2.png" alt="rails new" width="204" height="152" /><br />
</a><br />
<a href="rails3.png"><br />
<img src="rails3.png" alt="rails list" width="204" height="152" /><br />
</a><br />
<a href="rails3.png"><br />
<img src="rails3.png" alt="rails list" width="204" height="152" /><br />
</a></p>
<h2><a name="conclusions"></a>Conclusions</h2>
<ul>
<li>It would be nice if ASP.NET MVC, Catalyst and Django created<br />
controllers that pushed the idea of using an app level<br />
base class like Cake and Rails do. I hear &#8220;use a base class&#8221;<br />
all the time in Catalyst discussions  in response to people<br />
wanting to create plugins. It  would be better for the new<br />
user to be led to practice what is preached.</li>
<li>While Rails scaffolding is nice and can be customized,<br />
it can&#8217;t be added to and existing controller/model. This means<br />
you need to know that you have to use it first. It would be<br />
much more useful if you could at least point it to an existing<br />
model.</li>
<li>CakePHP scaffolding is quite nice but it&#8217;s not very easy<br />
to customize like the Django admin section is. It is pretty<br />
easy to move into an admin set of controllers namespace though.</p>
<p>Ruby strikes a nice balance here as well. It generates CRUD scaffold for you, but in the end it&#8217;s just code on your app you can tweak any way you want.</li>
<li>Django admin scaffolding is the bomb. It&#8217;s in it&#8217;s own<br />
directory with authentication already rolled in.</li>
<li>CRUD scaffolding in Catalyst is mostly non existent<br />
for the DBIx::Class user (the ORM pushed in the tutorials)<br />
unless you&#8217;re willing to write some glue classes. If you&#8217;re running<br />
Rose::DB as your ORM, you&#8217;re golden. The ListFramework is promising and so is the CatalystX::CRUD stuff. I imagine that given some time, it will have strong CRUD like Django does.</li>
<li>.NET MVC falls flat in the CRUD Scaffold arena.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/mvc-marathon-part-3-creating-a-restaurants-controller-and-view/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MVC Marathon Part 2: Creating a Database and Model</title>
		<link>http://chrislaco.com/articles/mvc-marathon-part-2-creating-a-database-and-model/</link>
		<comments>http://chrislaco.com/articles/mvc-marathon-part-2-creating-a-database-and-model/#comments</comments>
		<pubDate>Sun, 13 Jul 2008 00:42:10 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[catalyst]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[merb]]></category>
		<category><![CDATA[mojo]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=6</guid>
		<description><![CDATA[Welcome to part 2 of MVC Marathon, a multipart excursion into creating an application in the major MVC frameworks available today. The source code for this part can be found here: http://github.com/claco/mvc-marathon/tree/part2/ Part 2: Creating a Database and Model Now that we have &#8230; <a href="http://chrislaco.com/articles/mvc-marathon-part-2-creating-a-database-and-model/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Welcome to part 2 of <a href="/articles/mvc-marathon/">MVC Marathon</a>, a multipart excursion into creating an application in the major MVC frameworks available today.</p>
<p>The source code for this part can be found here: <a href="http://github.com/claco/mvc-marathon/tree/part2/">http://github.com/claco/mvc-marathon/tree/part2/</a></p>
<p><span id="more-6"></span></p>
<h2>Part 2: Creating a Database and Model</h2>
<p>Now that we have a bunch of shiny new applications, we need to add a database to store our data, and a model to get at the data. For this part of the project, I&#8217;m going to try and bypass the need to deal with mysql/postgres/mssql issues and go with the most simple database I can: SQLite. While there is an SQLite driver for .NET, it does not yet play well with LINQ, so in ASP.NET I&#8217;ll be using SQL Server Express, which is part of the Studio install.</p>
<p>Since BurningPlate is a website about restaurants peppery hot menu items, we&#8217;re going to need a table to hold restaurant records. For now, the restaurants table needs only two fields: a record id and the name of the restaurant. The id field will be an auto-increment integer field and the name will be a 100 character field, both disallowing nulls.</p>
<p>In general, there are at two distinct ways to go about creating a new database its models.</p>
<p>First, all of the frameworks can simply be pointed at a database, told the name of tables and the models will inspect the schema and provide their own glue. In this scenario, the user is generally expected to alter the database, and have the model follow along.</p>
<p>The second method is really a reverse of the first. You create a schema in a generic language, or define your models and the database schema is generated and/or deployed to the database from the models themselves. Some frameworks like Ruby and Catalyst go one step further and allow that schema to be versioned, allowing easier upgrades/downgrades of the schema itself.</p>
<p>Since the second method is more exciting and offers more insight into how the various frameworks work, that will be what I do when the framework supports it.</p>
<p>Today we&#8217;ll explore how to create database and model in the various frameworks and explore the difference between them. You can jump to any specific framework using the links below.</p>
<ul>
<li><a href="#aspnetmvc">ASP.NET MVC</a></li>
<li><a href="#cakephp">CakePHP</a></li>
<li><a href="#catalyst">Catalyst</a></li>
<li><a href="#django">Django</a></li>
<li><a href="#rails">Ruby on Rails</a></li>
<li><a href="#conclusions">Conclusions</a></li>
</ul>
<h2><a name="aspnetmvc"></a>ASP.NET MVC</h2>
<p>With .NET 3.0 came LINQ, a new way to model database tables to strongly typed classes. This new method works much like the strongly typed datasets of old from the user perspective. Pick a table, map it to properties, map stored procedures for actions, then code away with strongly types classes representing your table data.</p>
<p>Unfortunately, ASP.NET MVC is a database-first framework. You must create the database and tables first. You can&#8217;t create the LINQ to SQL classes first and then create the database/tables from the LINQ classes. Maybe this will change with IronRuby.</p>
<h3>Creating the Database</h3>
<p>First, to create the database, Right-click the project file and choose &#8220;Add New Item&#8221;. Select &#8220;SQL Server Database&#8221;. Name the new database &#8220;BurningPlate.mdf&#8221; and click &#8220;Add&#8221;.</p>
<p><a href="aspnet1.png"><br />
<img src="aspnet1.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>Studio gripes about the file type and asks us if we want to put it it in our App_Data folder. Say &#8220;Yes&#8221; and it will. <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><a href="aspnet2.png"><br />
<img src="aspnet2.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p><a href="aspnet3.png"><br />
<img src="aspnet3.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>Now that we have a database, we need to create a new table. Double-click the new database and a Server Explorer will open on the left.</p>
<p><a href="aspnet4.png"><br />
<img src="aspnet4.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>Right-click on he Tables folder and choose &#8220;Add New Table&#8221;</p>
<p><a href="aspnet5.png"><br />
<img src="aspnet5.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>As we&#8217;ll see in other frameworks, LINQ to SQL classes have some singular/pluralization (&#8216;inflection&#8217;) naming magic to them. In our case, a restaurants table is a collection of rows, where each row is a restaurant. For now, lets name our table &#8220;Restaurants&#8221;.</p>
<p><a href="aspnet6.png"><br />
<img src="aspnet6.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>Now that we have a new table, we&#8217;ll add our id and name columns. We&#8217;ll follow the frameworks naming conventions and add an Id field, setting the type to INT. We also need to set the column property &#8220;Is Identity&#8221; below to make it an auto-increment field. Add the Name column, setting the type to VARCHAR and change the length to 100 and click the Save button.</p>
<p><a href="aspnet9.png"><br />
<img src="aspnet9.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<h3>Creating the Model</h3>
<p>Now that we have our database and table, we need to create the model class to access that data. To create our new model, Right-click the projects Models folder and select &#8220;Add New Item&#8221;. Choose &#8220;LINQ to SQL Classes&#8221; and name it &#8220;BurningPlate.dbml&#8221;.</p>
<p><a href="aspnet7.png"><br />
<img src="aspnet7.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>Now all we have to do is drag the Restaurants table from the Server Explorer into the new BurningPlate.dbml designer window.</p>
<p><a href="aspnet8.png"><br />
<img src="aspnet8.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<p>That&#8217;s it. We have a new database, table and model for use in ASP.NET MVC applications. Notice how the model was given the name &#8220;Restaurant&#8221;. That&#8217;s inflection. You can of course name your model anything you want, and you can alter the name of the table it points to at any time.</p>
<h2><a name="cakephp"></a>CakePHP</h2>
<p>Like most other frameworks and unlike the ASP.NET MVC framework, CakePHP can create database tables from a schema definition using it&#8217;s own generic descriptive methods/properties to describe tables. Before we do that, we need to continue the database configuration that we skipped when we<a href="/blog/mvc-marathon-part-1-creating-a-new-application/#cakephp"> created</a> or new CakePHP application.</p>
<p>To finish configuring our database information, just rerun<kbd> cake bake</kbd> within our app directory.</p>
<pre class="brush:shell">Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : Burningplate
Path: /Users/claco/mvc-marathon/cakephp/Burningplate
---------------------------------------------------------------
Your database configuration was not found. Take a moment to create one.
---------------------------------------------------------------
Database Configuration:
---------------------------------------------------------------
Name:
[default] &gt;
Driver: (db2/firebird/mssql/mysql/mysqli/odbc/oracle/postgres/sqlite/sybase)
[mysql] &gt; sqlite
Persistent Connection? (y/n)
[n] &gt;
Database Host:
[localhost] &gt;
Port?
[n] &gt;
User:
[root] &gt;
Password:
&gt;
The password you supplied was empty. Use an empty password? (y/n)
[n] &gt; y
Database Name:
[cake] &gt; burning_plate.db
Table Prefix?
[n] &gt;
Table encoding?
[n] &gt;

---------------------------------------------------------------
The following database configuration will be created:
---------------------------------------------------------------
Name:         default
Driver:       sqlite
Persistent:   false
Host:         localhost
User:         root
Pass:
Database:     burning_plate.db
---------------------------------------------------------------
Look okay? (y/n)
[y] &gt;
Do you wish to add another database configuration?
[n] &gt;

Creating file /Users/claco/mvc-marathon/cakephp/Burningplate/config/database.php
Wrote /Users/claco/mvc-marathon/cakephp/Burningplate/config/database.php</pre>
<p><strong>Note:</strong> We&#8217;ve given the database path as a relative path. The <kbd>cake</kbd> command line utils consider the root path of my app as the BurningPlate folder, but CakePHP under Apache thinks the root path is<kbd> BurningRiver/webroot</kbd>. This means when using a relative database path, one or the other won&#8217;t find the database. Not very portable by default. In order to fix that, I had to add the following to the database config:</p>
<pre class="brush:php">class DATABASE_CONFIG {
  function __construct() {
  $this-&gt;default['database'] = APP . $this-&gt;default['database'];
}</pre>
<h3>Creating the Database</h3>
<p>Now that we&#8217;ve told CakePHP where and what database we&#8217;re going to use, we need to create our restaurants table. Since we&#8217;re trying to always create the tables from the models, we&#8217;ll do that first.</p>
<p>CakePHP uses inflection to automagically wire up default code and save the user as much time as possible <em>if</em> you follow certain naming conventions. To create tables from definitions, you need to create definitions in the<kbd> config/sql/schema.php</kbd> file. Of course, you can also point a model at any old table and it will work. Schemas aren&#8217;t mandatory.</p>
<p>As above, we want a table that has an id integer as the primary key and a name varchar field, both not null. Unlike ASP.NET where we have to tell the database that the pk field was auto and let it manage the pk auto increment, CakePHP assumes that any integer primary key field is an auto increment field and will manage the ids automatically. It will work as expected even if you happen to have an existing table where the pk is defined as auto increment. You can even use UUIDs for the primary key just by making that field a CHAR(36) instead. But I digress&#8230;</p>
<pre class="brush:php">class BurningPlateSchema extends CakeSchema {
  public $restaurants = array(
    'id' =&gt; array('type' =&gt; 'integer', 'null' =&gt; false, 'default' =&gt; NULL, 'key' =&gt; 'primary'),
    'name' =&gt; array('type' =&gt; 'string', 'null' =&gt; false, 'length' =&gt; 100),
    'indexes' =&gt; array('PRIMARY' =&gt; array('column' =&gt; 'id', 'unique' =&gt; 1))
  );
}</pre>
<p>Now that we have a schema defined, we need to create the table from the schema definition. Let&#8217;s see what the command help has to offer:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/cakephp/Burningplate $ cake schema
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : Burningplate
Path: /Users/claco/mvc-marathon/cakephp/Burningplate
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------
The Schema Shell generates a schema object from
the database and updates the database from the schema.
---------------------------------------------------------------
Usage: cake schema &lt;command&gt; &lt;arg1&gt; &lt;arg2&gt;...
---------------------------------------------------------------
Params:

-connection &lt;config&gt;
set db config &lt;config&gt;. uses 'default' if none is specified

-path &lt;dir&gt;
path &lt;dir&gt; to read and write schema.php.
default path: /Users/claco/mvc-marathon/cakephp/Burningplate/config/sql

-file &lt;name&gt;
file &lt;name&gt; to read and write.
default file: schema.php

-s &lt;number&gt;
snapshot &lt;number&gt; to use for run.

-dry
Perform a dry run on 'run' commands.
Queries will be output to window instead of executed.

-f
force 'generate' to create a new schema.
Commands:

schema help
shows this help message.

schema view
read and output contents of schema file

schema generate
reads from 'connection' writes to 'path'
To force generation of all tables into the schema, use the -f param.

schema dump &lt;filename&gt;
dump database sql based on schema file to filename in schema path.
if filename is true, default will use the app directory name.

schema run create &lt;schema&gt; &lt;table&gt;
drop tables and create database based on schema file
optional &lt;schema&gt; arg for selecting schema name
optional &lt;table&gt; arg for creating only one table
pass the -s param with a number to use a snapshot
To see the changes, perform a dry run with the -dry param

schema run update &lt;schema&gt; &lt;table&gt;
alter tables based on schema file
optional &lt;schema&gt; arg for selecting schema name.
optional &lt;table&gt; arg for altering only one table.
To use a snapshot, pass the -s param with the snapshot number
To see the changes, perform a dry run with the -dry param</pre>
<p>Now we&#8217;ll create our new table:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/cakephp/Burningplate $ cake schema run create
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : Burningplate
Path: /Users/claco/mvc-marathon/cakephp/Burningplate
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------

The following tables will be dropped.
restaurants

Are you sure you want to drop the tables? (y/n)
[n] &gt; n

The following tables will be created.
restaurants

Are you sure you want to create the tables? (y/n)
[y] &gt; y
Creating tables.
restaurants updated.
End create.</pre>
<h3>Creating the Model</h3>
<p>Now that we&#8217;ve created our table and database, we need to create a model. Once again we&#8217;ll use <kbd>cake bake</kbd> to get the job done.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/cakephp/Burningplate $ cake bake
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : Burningplate
Path: /Users/claco/mvc-marathon/cakephp/Burningplate
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[Q]uit
What would you like to Bake? (D/M/V/C/P/Q)
&gt; M
---------------------------------------------------------------
Bake Model
Path: /Users/claco/mvc-marathon/cakephp/Burningplate/models/
---------------------------------------------------------------
Possible Models based on your current database:
1. Restaurant
Enter a number from the list above, type in the name of another model, or 'q' to exit
[q] &gt; 1
Would you like to supply validation criteria for the fields in your model? (y/n)
[y] &gt; n
Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)? (y/n)
[y] &gt; n

---------------------------------------------------------------
The following Model will be created:
---------------------------------------------------------------
Name:       Restaurant
Associations:
---------------------------------------------------------------
Look okay? (y/n)
[y] &gt;

Baking model class for Restaurant...

Creating file /Users/claco/mvc-marathon/cakephp/Burningplate/models/restaurant.php
Wrote /Users/claco/mvc-marathon/cakephp/Burningplate/models/restaurant.php
Cake test suite not installed.  Do you want to bake unit test files anyway? (y/n)
[y] &gt; y

You can download the Cake test suite from http://cakeforge.org/projects/testsuite/

Baking test fixture for Restaurant...

Creating file /Users/claco/mvc-marathon/cakephp/Burningplate/tests/fixtures/restaurant_fixture.php
Wrote /Users/claco/mvc-marathon/cakephp/Burningplate/tests/fixtures/restaurant_fixture.php

Baking unit test for Restaurant...

Creating file /Users/claco/mvc-marathon/cakephp/Burningplate/tests/cases/models/restaurant.test.php
Wrote /Users/claco/mvc-marathon/cakephp/Burningplate/tests/cases/models/restaurant.test.php
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[Q]uit
What would you like to Bake? (D/M/V/C/P/Q)
&gt; Q</pre>
<p>CakePHP simply inspected the database, asked us which table to create a model for and created the necessary files, including tests.</p>
<p>Again, take note of the inflection here. We created a table called restaurants but the model created was Restaurant. If you were to manually created a model called Post, it would by default look for a table called posts. This can, of course, be overridden using the <samp>$useTable</samp> variable.</p>
<p>While there is a <kbd>cake console</kbd> that we could use to test out model and create a record, I couldn&#8217;t get it to work. It could connect to the database and find records, but it would not save a new record, even though it said &#8220;Saved record for Restaurant&#8221;.</p>
<h2><a name="catalyst"></a>Catalyst</h2>
<p>One of the Perl mantras is TIMTOWTDI: There is more than one way to do it. As such, Catalyst doesn&#8217;t come with database support out of the box. One reason for this is that there are more than one ORM packages for Perl and different reasons for an author to prefer one over the other. However, if you install the bundle referenced in the official tutorial, it will install of the necessary bits to do database work.</p>
<p>Much like CakePHP, you can have three distinct layers when using a database in Catalyst: The models generated from the schema, the schema describing the database and the database itself. You can choose to statically or dynamical create the schema from the database or do the reverse: create the database from the schema.</p>
<p>For this application, I&#8217;m going to use the new schema versioning built into <kbd>DBIx::Class</kbd>. Like CakePHP above, we&#8217;re going to create our schema first, deploy it to the database and then create models from our schema.</p>
<h3>Creating the Database</h3>
<p>Unlike CakePHP, we need to get down and dirty with writing some code manually to get our schema started and to maintain the schema versions and upgrades. This isn&#8217;t as bad as it sounds. Most of the heavy lifting is already coded for us. First, we need to create out schema file in <kbd>lib/BurningPlate/Scheme.pm</kbd></p>
<pre class="brush:perl">package BurningPlate::Schema;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
our $VERSION = 0;

__PACKAGE__-&gt;load_classes;
__PACKAGE__-&gt;load_components('+DBIx::Class::Schema::Versioned');
__PACKAGE__-&gt;upgrade_directory('sql/');
__PACKAGE__-&gt;backup_directory('sql/backups/');

1;</pre>
<p>In a nutshell, we&#8217;re telling DBIx::Class to load all BurningPlate::Schema::* child classes, load the versioning module, and place the schema upgrade/backup files in the given directories. Take note of the <kbd>$VERSION</kbd> variable. This is used by the versioning module to determine what version the schema is compared to the database versioning information table.</p>
<p>We could create our table schema and deploy the schema to the database. DBIx::Class currently considers any database without a versioning table to be the current version and will not upgrade anything even if it difference from your schema classes. Because of this, it&#8217;s better to consider version 1 a blank database and work up from there. We&#8217;re going to deploy what we have now before we add the restaurants table.</p>
<p>DBIx::Class doesn&#8217;t come with console scripts to generate schema versioning files or upgrade database. But because they&#8217;re simple method calls, they&#8217;re easy enough to create. First our schema sql script <kbd>script/burningplate_schema.pl</kbd></p>
<pre class="brush:perl">#!/usr/bin/perl -w
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use BurningPlate::Schema;

my $version = BurningPlate::Schema-&gt;schema_version;

BurningPlate::Schema-&gt;connect-&gt;create_ddl_dir(
  ['SQLite'],
  $version &gt; 1 ? $version : undef,
  'sql',
  $version ? $version-1 : $version
);</pre>
<p>The script basically loads the schema, finds the version, then calls <kbd>create_ddl_dir</kbd> to create sql ddl files from the current schema version. If the version is greater than 1, we also create an sql file that contains the difference between the versions. This is the heart of schema versioning. Database changes can be rolled out incrementally from one version to the next. We specified SQLite, but you can create multiple schemas at the same time for multiple database vendors.</p>
<p>Now, we&#8217;ll run the script to generate our version 1 ddl files.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*schema.pl
Your DB is currently unversioned. Please call upgrade on your schema to sync the DB.</pre>
<p>Now, in our sql directory, we have a new file:</p>
<pre class="brush:sql">--
-- Created by SQL::Translator::Producer::SQLite
-- Created on Tue Jul  8 19:44:05 2008
--
BEGIN TRANSACTION;

COMMIT;</pre>
<p>Since we have no tables yet, we have no sql to deploy but we must still run an upgrade to install the versioning table and the current version number. Since we have no console script to do that ,  we&#8217;ll create one in <kbd>script/burningplate_upgrade.pl</kbd></p>
<pre class="brush:perl">#!/usr/bin/perl -w
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use BurningPlate::Schema;

BurningPlate::Schema-&gt;connect(@ARGV)-&gt;upgrade;</pre>
<p>In this script, we simply connect to the specified database and upgrade it to the latest version. Now we can deploy our first version and the versioning tables.</p>
<pre>claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*upgrade.pl dbi:SQLite:burning_plate.db
Your DB is currently unversioned. Please call upgrade on your schema to sync the DB.</pre>
<p>Now, let&#8217;s define our restaurants schema and deploy it to the database. First, we need to increase the schema $VERSION to 2. Next, we&#8217;ll create <kbd>BurningPlate/Schema/Restaurant.pm</kbd></p>
<pre class="brush:perl">package BurningPlate::Schema::Restaurant;
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__-&gt;load_components('Core');
__PACKAGE__-&gt;table('restaurants');
__PACKAGE__-&gt;add_columns(
  id =&gt; {
    data_type =&gt; 'INT',
    is_nullable =&gt; 0,
    is_auto_increment =&gt; 1
  },
  name =&gt; {
    data_type =&gt; 'VARCHAR',
    size =&gt; 100,
    is_nullable =&gt; 0
  }
);
__PACKAGE__-&gt;set_primary_key('id');

1;</pre>
<p>Much like the CakePHP schema, we are telling the schema the names and properties of the columns and a primary key. Now that we have a complete schema, we need to deploy it to a database. First, we&#8217;ll generate the version 2 sql.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*schema.pl</pre>
<p>Now, if you look in the sql folder, we have two new files. First, we have <kbd>BurningPlate-Schema-2-SQLite.sql</kbd>. This contains the entire schema as it exists in version 2 if we were to deploy the entire schema from scratch.</p>
<pre class="brush:sql">--
-- Created by SQL::Translator::Producer::SQLite
-- Created on Tue Jul  8 22:52:12 2008
--
BEGIN TRANSACTION;
--
-- Table: restaurants
--
DROP TABLE restaurants;
CREATE TABLE restaurants (
  id INTEGER PRIMARY KEY NOT NULL,
  name VARCHAR(100) NOT NULL
);

COMMIT;</pre>
<p>We also have a second file: <kbd>BurningPlate-Schema-1-2-SQLite.sql</kbd>. This file contains all the sql necessary to upgrade a database from version to version 2.</p>
<pre class="brush:sql">-- Convert schema 'sql/BurningPlate-Schema-1-SQLite.sql' to 'sql/BurningPlate-Schema-2-SQLite.sql':

BEGIN;

CREATE TABLE restaurants (
  id INTEGER PRIMARY KEY NOT NULL,
  name VARCHAR(100) NOT NULL
);

COMMIT;</pre>
<p>Later on if we were to add a column to version 3, the 2-&gt;3 file would contain an ALTER TABLE statement. Now, we just need to deploy the new version to the database using the same command we ran before:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/*upgrade.pl dbi:SQLite:burning_plate.db
Versions out of sync. This is 2, your database contains version 1, please call upgrade on your Schema.</pre>
<p>The warnings are a little misleading. They&#8217;re generated by the call to connect() which do before calling upgrade.</p>
<h3>Creating the Model</h3>
<p>Now that we have our schema and database, we need to create a model to access that data. There are many ways to do this in Catalyst. One can manually create a model and load the schema directly or you can bypass the schema class and write database access code to use the database directly. Let&#8217;s see what the create script has to say:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/burningplate_create.pl
Usage:
burningplate_create.pl [options] model|view|controller name [helper]
[options]

Options:
-force        don't create a .new file where a file to be created exists
-mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
-help         display this help and exits

Examples:
burningplate_create.pl controller My::Controller
burningplate_create.pl controller My::Controller BindLex
burningplate_create.pl -mechanize controller My::Controller
burningplate_create.pl view My::View
burningplate_create.pl view MyView TT
burningplate_create.pl view TT TT
burningplate_create.pl model My::Model
burningplate_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic
dbi:SQLite:/tmp/my.db
burningplate_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static
dbi:Pg:dbname=foo root 4321

See also:
perldoc Catalyst::Manual
perldoc Catalyst::Manual::Intro</pre>
<p>The easiest way to use our schema  as a model without writing code is to use <kbd>Catalyst::Model::DBIC::Schema</kbd> listed above. This will automatically load a schema class and create a model for each table (resultsource in DBIC speak) found. To create our model, we simply call the create script, passing in the appropriate options:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst/BurningPlate $ script/burningplate_create.pl model DB DBIC::Schema BurningPlate::Schema dbi:SQLite:burning_plate.db
exists "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/Model"
exists "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../lib/BurningPlate/Model/DB.pm"
created "/Users/claco/mvc-marathon/catalyst/BurningPlate/script/../t/model_DB.t"</pre>
<p>Just like CakePHP, it generated a model file and created a test file for us. If we reload the application, we now see that the new model is loaded and ready for action.</p>
<pre class="brush:shell">[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| BurningPlate::Controller::Root                                  | instance |
| BurningPlate::Model::DB                                         | instance |
| BurningPlate::Model::DB::Restaurant                             | class    |
'-----------------------------------------------------------------+----------'</pre>
<p>In contrast to CakePHP, there is no inflection magic here. Name your model and your table whatever you want without worry of breaking things without really knowing.</p>
<p>Unlike CakePHP and as we&#8217;ll see later, Django and Rails, there is no interactive console to work with your newly created models. But there is a utility called <kbd>dbicadmin</kbd> that can be used to perform CRUD operations with the schema. On the down side, the schema doesn&#8217;t hold the database connection information; the model does. So using the utility isn&#8217;t practical in this case without altering code.</p>
<h2><a name="django"></a>Django</h2>
<p>Django takes a more compact approach than CakePHP and Catalyst w/ DBIx::Class. In Django, there is no separation between models and the schema. In fact, all of the models are defined in one single models.py class instead of putting separate classes in a models directory like Catalyst/CakePHP do.</p>
<h3>Creating the Model</h3>
<p>Creating models in Django is just a matter of creating the models.py file. You also get a modesl.py file for free when you run <kbd>manage.py startapp</kbd>, which creates a new application sudirectory in the project we previously created. Since we&#8217;re not really creating multiple apps right now, we&#8217;ll just create the models.py file in the project root.</p>
<pre class="brush:python">from django.db import models

class Restaurant(models.Model):
  id = models.AutoField(primary_key=True)
  name = models.CharField(max_length=100)</pre>
<p>In the file above, we created a new Restaurant class and defined the id and name fields. AutoField implies a code managed (not db managed) auto increment int field, and we&#8217;ve set primary key and the name length.</p>
<h3>Creating the Database</h3>
<p>Now that we have a model, we need to create a database from it First, we need to tell Django how to conect to the new database by changing <kbd>settinga.py</kbd></p>
<pre class="brush:python">DATABASE_ENGINE = 'sqlite3'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
DATABASE_NAME = 'burning_plate.db'    # Or path to database file if using sqlite3.</pre>
<p>While we&#8217;re in here, we need to tell this Django project that it also should load our new BurningPlate application, which is now only a models.py file. If we wanted to use other apps in our project, we could just laod them here.</p>
<pre class="brush:python">INSTALLED_APPS = (
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.sites',
  'BurningPlate'
)</pre>
<p>Now, to create our database, we just run the syncd command.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/django/BurningPlate $ python manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): no
Installing index for auth.Permission model
Creating table BurningPlate_restaurant</pre>
<p>That&#8217;s all there is to it. Django created a bunch of auth related tables and then our restaurant table. Let&#8217;s take a gander at the schema:</p>
<pre class="brush:shell">CREATE TABLE "BurningPlate_restaurant" (
  "id" integer NOT NULL PRIMARY KEY,
  "name" varchar(100) NOT NULL
);</pre>
<p>Django also doesn&#8217;t play any inflection games. The model name is the table name. However, since Django projects are assumed to have multiple apps within them, the name of the app has been prepended to the table name to avoid name collisions. You can override the table name using the <kbd>db_table</kbd> variable.</p>
<p>Now, let&#8217;s fire up the interactive console and test out our model.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/django/BurningPlate $ python manage.py shell
Python 2.5.1 (r251:54863, Oct 17 2007, 22:46:25)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
&gt;&gt;&gt; from BurningPlate .models import Restaurant
&gt;&gt;&gt; Restaurant.objects.all()
[]
&gt;&gt;&gt; r = Restaurant(name="Azteca")
&gt;&gt;&gt; r.save()
&gt;&gt;&gt; r.id
1
&gt;&gt;&gt; r.name
'Azteca'
&gt;&gt;&gt; Restaurant.objects.all()
[&lt;Restaurant: Restaurant object&gt;]
&gt;&gt;&gt; quit()</pre>
<h2><a name="rails"></a>Ruby on Rails</h2>
<p>Rails has migrations which is a lot like the Catalyst/DBIx::Class schema versioning with a little more glue around the edges to make thing go quicker. Like CakePHP and Catalyst, the model, schema (or migrations) and the database are three separate layers. Unlike Django models, Rails (ActiveRecord based) models don&#8217;t describe the schema. Instead, they operate by dynamically inspecting the table they&#8217;re working on.</p>
<h3>Creating the Model</h3>
<p>Like Catalyst and CakePHP, we can use the console scripts to create a new model and do all sorts of other things. First, let&#8217;s see what generate tells us.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails/BurningPlate $ script/generate
Usage: script/generate generator [options] [args]

Rails Info:
-v, --version                    Show the Rails version number and quit.
-h, --help                       Show this help message and quit.

General Options:
-p, --pretend                    Run but do not make any changes.
-f, --force                      Overwrite files that already exist.
-s, --skip                       Skip files that already exist.
-q, --quiet                      Suppress normal output.
-t, --backtrace                  Debugging: show backtrace on errors.
-c, --svn                        Modify files with subversion. (Note: svn must be in path)

Installed Generators
Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration

More are available at http://rubyonrails.org/show/Generators
1. Download, for example, login_generator.zip
2. Unzip to directory /Users/claco/.rails/generators/login
to use the generator with all your Rails apps
or to /Users/claco/mvc-marathon/rails/BurningPlate/lib/generators/login
to use with this app only.
3. Run generate with no arguments for usage information
script/generate login

Generator gems are also available:
1. gem search -r generator
2. gem install login_generator
3. script/generate login</pre>
<p>Now, let&#8217;s check out the model option.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails/BurningPlate $ script/generate model
Usage: script/generate model ModelName [field:type, field:type]

Options:
--skip-timestamps            Don't add timestamps to the migration file for this model
--skip-migration             Don't generate a migration file for this model
--skip-fixture               Don't generation a fixture file for this model

Rails Info:
-v, --version                    Show the Rails version number and quit.
-h, --help                       Show this help message and quit.

General Options:
-p, --pretend                    Run but do not make any changes.
-f, --force                      Overwrite files that already exist.
-s, --skip                       Skip files that already exist.
-q, --quiet                      Suppress normal output.
-t, --backtrace                  Debugging: show backtrace on errors.
-c, --svn                        Modify files with subversion. (Note: svn must be in path)

Description:
Stubs out a new model. Pass the model name, either CamelCased or
under_scored, and an optional list of attribute pairs as arguments.

Attribute pairs are column_name:sql_type arguments specifying the
model's attributes. Timestamps are added by default, so you don't have to
specify them by hand as 'created_at:datetime updated_at:datetime'.

You don't have to think up every attribute up front, but it helps to
sketch out a few so you can start working with the model immediately.

This generates a model class in app/models, a unit test in test/unit,
a test fixture in test/fixtures/singular_name.yml, and a migration in
db/migrate.

Examples:
`./script/generate model account`

creates an Account model, test, fixture, and migration:
Model:      app/models/account.rb
Test:       test/unit/account_test.rb
Fixtures:   test/fixtures/accounts.yml
Migration:  db/migrate/XXX_add_accounts.rb

`./script/generate model post title:string body:text published:boolean`

creates a Post model with a string title, text body, and published flag.</pre>
<p>We could pass in our column definitions in one shot, but where&#8217;s the fun in that? Let&#8217;s create a new model.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails/BurningPlate $ script/generate model Restaurant
exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/restaurant.rb
create  test/unit/restaurant_test.rb
create  test/fixtures/restaurants.yml
create  db/migrate
create  db/migrate/001_create_restaurants.rb</pre>
<p>Just like CakePHP and Catalyst, the script was kind enough to create some test files for the new model. It also generated a migration file for us. Let&#8217;s have a look:</p>
<pre class="brush:python">class CreateRestaurants &lt; ActiveRecord::Migration
  def self.up
    create_table :restaurants do |t|
      t.column :name, :string, :limit=&gt;100, :null=&gt;false
      t.timestamps
    end
  end

  def self.down
    drop_table :restaurants
  end
end</pre>
<p>Unlike DBIx::Class schema version files which are sql based, Rails migrations are based on code. Up describes what to do when migrating from an older version to the current version. Down describes what to do when migrating form the current version to the previous version. For version 1 [from 0].</p>
<h3>Creating the Database</h3>
<p>Now that we have our model, let&#8217;s create our database from it. We need to use rake, Rubys make, to migrate the database.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails/BurningPlate $ rake db:migrate
(in /Users/claco/mvc-marathon/rails/BurningPlate)
== 1 CreateRestaurants: migrating =============================================
-- create_table(:restaurants)
-&gt; 0.0025s
== 1 CreateRestaurants: migrated (0.0027s) ====================================</pre>
<p>Notice the table name: restaurants. Our old friend inflection is back. We also didn&#8217;t have to setup our database connection information. The default database.yml config already has a development database setup in <kbd>bd/development.sqlite3</kbd>.</p>
<p>Another item of interest. If we take a look at the table that was create, you&#8217;ll notice something that wasn&#8217;t in our model:</p>
<pre class="brush:sql">CREATE TABLE restaurants ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  "name" varchar(100) NOT NULL,
  "created_at" datetime DEFAULT NULL,
  "updated_at" datetime DEFAULT NULL);</pre>
<p>When Rails creates a table from a model, it always adds the id, create_at and updated_at fields for automatically set create and updated dates when each record is created/modified. CakePHP will also maintain create/updated fields but it will not declare them for you when you create the tables.</p>
<p>Like most of the other frameworks, you can use an interactive console to try out your new models.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails/BurningPlate $ script/console
Loading development environment (Rails 2.0.2)
&gt;&gt; Restaurant.create(:name =&gt; 'Azteca')
=&gt; #&lt;Restaurant id: 1, name: "Azteca", created_at: "2008-07-08 22:29:32", updated_at: "2008-07-08 22:29:32"&gt;
&gt;&gt; r = Restaurant.find_by_id(1)
=&gt; #&lt;Restaurant id: 1, name: "Azteca", created_at: "2008-07-08 22:29:32", updated_at: "2008-07-08 22:29:32"&gt;
&gt;&gt; r.name
=&gt; "Azteca"
&gt;&gt; quit()</pre>
<h2><a name="conclusions"></a>Conclusions</h2>
<ul>
<li>Catalyst is my alma mater. Perl is my favorite language, and I appreciate that there are many ways to do the same thing and nothing is forced upon me. However, as DBIx::Class is pushed in the tutorials, it is really the defacto ORM for Catalyst. As such, there really needs to be some glue to round off the sharp edges.If DBIC is installed or there is a command line option to request it,<kbd> catalyst</kbd> should generate a Schema class, directories for the sql versioning and a helper script or two to help generate schema versioning files and update/deploy them. This would go a long way to improving the new user experience.</li>
<li>The CakePHP interactive console seems to be a bit immature. It doesn&#8217;t appear to add items, or at least my install wasn&#8217;t very happy. The other thing I&#8217;m not sure I can accept is that models return data, not objects.The schema management only deploys new tables. It doesn&#8217;t deploy changes. It would be nice to see some sort of versioning or migrations like Catalyst/Rails.The fact that the console utilities and webroot look in two different places for a database path is a drag. It makes things less portable, esp if you have to put in a full path.</li>
<li>Compared to Rails migrations and DBIC versioning, Django feels week like CakePHP when it comes to syncing the model to the database.I love that apps have been given consideration as movable reusable parts within many projects. This is something that is a little more obtuse in other frameworks.</li>
<li>I dig Rails migrations. While I can also have custom code when upgrading schema versions in Catalyst, migrations feel more natural to me. I like that upgrades and downgrades feel like they get equal attention and you can migrate to any version by number.The magic adding of the id column and the created/updated columns scares me a little. If I already have a created column instead of a created_At column, well, that could lead to issues.</li>
<li>Inflections seem unnecessary to me, esp if you don&#8217;t speak Engrish as the primary language. But, to their credit, at least Rails and CakePHP give you control over the inflections if you need. Good Magic is magic that can be customized.</li>
<li>ASP.NET/ADO/SQL aren&#8217;t really build for versioning or for deploying a database schema from the models. To me, this makes deploying new versions a little more painful if you are stuck writing your alter scripts and such.</li>
<li>One of the things I like about CakePHP is that by default, new models and controllers inherit from an app local model and controller base class. Rails controllers also do this. This is easy to do in Catalyst, but it&#8217;s not forced upon you by default. Maybe it should be since I hear a lot of &#8220;use a base class&#8221; responses when people ask about creating plugins.Ruby has more of a mixin model rather than multiple inheritance. Personally, I&#8217;d prefer that Rails models also inherited from an app local base class, then mixin-ed ActiveRecord on top of that.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/mvc-marathon-part-2-creating-a-database-and-model/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MVC Marathon Part 1: Creating a New Application</title>
		<link>http://chrislaco.com/articles/mvc-marathon-part-1-creating-a-new-application/</link>
		<comments>http://chrislaco.com/articles/mvc-marathon-part-1-creating-a-new-application/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 02:24:19 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[catalyst]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[merb]]></category>
		<category><![CDATA[mojo]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=5</guid>
		<description><![CDATA[Welcome to part 1 of MVC Marathon, a multipart excursion into creating an application in the major MVC frameworks available today. The source code for this part can be found here: http://github.com/claco/mvc-marathon/tree/part1-creating-a-new-application/ Part 1: Creating a New Application The first step in any &#8230; <a href="http://chrislaco.com/articles/mvc-marathon-part-1-creating-a-new-application/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Welcome to part 1 of <a href="/articles/mvc-marathon/">MVC Marathon</a>, a multipart excursion into creating an application in the major MVC frameworks available today.</p>
<p>The source code for this part can be found here: <a href="http://github.com/claco/mvc-marathon/tree/part1-creating-a-new-application/">http://github.com/claco/mvc-marathon/tree/part1-creating-a-new-application/</a></p>
<p><span id="more-5"></span></p>
<h2>Part 1: Creating a New Application</h2>
<p>The first step in any new application is creating a directory structure and any necessary configuration files. Fortunately, most of the frameworks now a days provide ready made templates or scripts to make getting started as easy as possible.</p>
<p>Today we&#8217;ll explore how to create and run a new application in the various frameworks and explore the difference between them. You can jump to any specific framework using the links below. For this series of articles, I&#8217;m going to assume that you already have the basic frameworks installed.</p>
<ul>
<li><a href="#aspnetmvc">ASP.NET MVC</a></li>
<li><a href="#cakephp">CakePHP</a></li>
<li><a href="#catalyst">Catalyst</a></li>
<li><a href="#django">Django</a></li>
<li><a href="#rails">Ruby on Rails</a></li>
<li><a href="#conclusions">Conclusions</a></li>
</ul>
<h2><a name="aspnetmvc"></a>ASP.NET MVC</h2>
<h3>Creating the Application</h3>
<p>The easiest way to create a new ASP.NET MVC application is using the application templates in Visual Studio. To do that, simply select <kbd>File -&gt; New -&gt; Project</kbd>:</p>
<p><a href="aspnetmvc-create.png"><br />
<img src="aspnetmvc-create.png" alt="aspnetmvc create" width="204" height="152" /><br />
</a></p>
<p>In the New Project window, select Visual C# as the language. Select the ASP.NET MVC Web Application, type in the name of the new application, in our case, BurningPlate and click OK. Visual Studio will create a new mvc application looking something like this:</p>
<p><a href="aspnetmvc-create2.png"><br />
<img src="aspnetmvc-create2.png" alt="aspnetmvc create" width="204" height="152" /><br />
</a></p>
<p>Like nearly all MVC applications, we have the usual Controllers, Models and Views folders along with some other .NET specific folders.</p>
<h3>Running the Application</h3>
<p>To run your new application, simply hit <kbd>F5</kbd>, which will start the application in Visual Studios local web server. This means no messing with IIS until you have to. If all goes well, you should end up with the default home page:</p>
<p><a href="aspnetmvc.png"><br />
<img src="aspnetmvc.png" alt="aspnetmvc run" width="204" height="152" /><br />
</a></p>
<h2><a name="cakephp"></a>CakePHP</h2>
<p>There seems to be at least two ways to create a new CakePHP application. The blog example on the CakePHP website say to just download and extract the latest version and start creating your application in the <kbd>app</kbd> subfolder. This seems like a good approach if you&#8217;re creating app to put on a server where you don&#8217;t know or control the version or even if CakePHP is installed.</p>
<p>If you&#8217;re running your own server, you can setup your env to point to a centralized install of CakePHP and use <kbd>cake</kbd> to create apps that reference the installed software. We&#8217;ll do the latter as it closely follows how the other frameworks work.</p>
<h3>Creating the Application</h3>
<p>First, let&#8217;s see what <kbd>cake</kbd> tells us when we run it:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon $ cake
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
Current Paths:
-app: mvc-marathon
-working: /Users/claco/mvc-marathon
-root: /Users/claco
-core: /sw/cakephp/

Changing Paths:
your working path should be the same as your application path
to change your path use the '-app' param.
Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp

Available Shells:

vendors/shells/:
- none

cake/console/libs/:
acl
api
bake
console
i18n
schema
testsuite

To run a command, type 'cake shell_name [args]'
To get help on a specific command, type 'cake shell_name help'</pre>
<p>Next, let&#8217;s run <kbd>cake bake help</kbd> to see what our options are:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon $ cake bake help
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : mvc-marathon
Path: /Users/claco/mvc-marathon
---------------------------------------------------------------
CakePHP Bake:
---------------------------------------------------------------
The Bake script generates controllers, views and models for your application.
If run with no command line arguments, Bake guides the user through the class
creation process. You can customize the generation process by telling Bake
where different parts of your application are using command line arguments.
---------------------------------------------------------------
Usage: cake bake &lt;command&gt; &lt;arg1&gt; &lt;arg2&gt;...
---------------------------------------------------------------
Params:
-app &lt;path&gt; Absolute/Relative path to your app folder.

Commands:

bake help
shows this help message.

bake all &lt;name&gt;
bakes complete MVC. optional &lt;name&gt; of a Model

bake project &lt;path&gt;
bakes a new app folder in the path supplied
or in current directory if no path is specified

bake plugin &lt;name&gt;
bakes a new plugin folder in the path supplied
or in current directory if no path is specified.

bake db_config
bakes a database.php file in config directory.

bake model
bakes a model. run 'bake model help' for more info

bake view
bakes views. run 'bake view help' for more info

bake controller
bakes a controller. run 'bake controller help' for more info</pre>
<p>Since we&#8217;re starting from scratch, let&#8217;s give <kbd>project</kbd> a whirl:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/cakephp $ cake bake BurningPlate
Welcome to CakePHP v1.2.0.7296 RC2 Console
---------------------------------------------------------------
App : cakephp
Path: /Users/claco/mvc-marathon/cakephp
---------------------------------------------------------------
Bake Project
Skel Directory: /sw/cakephp/cake/console/libs/templates/skel
Will be copied to: /Users/claco/mvc-marathon/cakephp/BurningPlate
---------------------------------------------------------------
Look okay? (y/n/q)
[y] &gt;
Do you want verbose output? (y/n)
[n] &gt;
---------------------------------------------------------------
Created: BurningPlate in /Users/claco/mvc-marathon/cakephp/BurningPlate
---------------------------------------------------------------

Creating file /Users/claco/mvc-marathon/cakephp/BurningPlate/views/pages/home.ctp
Wrote /Users/claco/mvc-marathon/cakephp/BurningPlate/views/pages/home.ctp
Welcome page created
Random hash key created for 'Security.salt'
CAKE_CORE_INCLUDE_PATH set to /sw/cakephp in webroot/index.php
CAKE_CORE_INCLUDE_PATH set to /sw/cakephp in webroot/test.php
Remember to check these value after moving to production server
Your database configuration was not found. Take a moment to create one.
---------------------------------------------------------------
Database Configuration:
---------------------------------------------------------------
Name:
[default] &gt; ^C</pre>
<h3>Running the Application</h3>
<p>At first glance, there appears to be no development server for CakePHP applications. There is a script called<a href="http://bakery.cakephp.org/articles/view/the-cakephp-instaweb-webserver"> cakephp-instaweb</a> available on the CakePHP website geared for this purpose. Ironically, the script relies on Python to get the job done. When I ran it, it appeared to work, but all I ever got when trying to hit it with the browser was a CGI Script Error carping about headers.</p>
<p>The second option is to run this application under Apache. Since I&#8217;m on a MacBook Pro and OSX includes a running version of Apache and PHP, all I needed to do was place the BurningPlate directory in my Sites folder. After hitting <kbd>http://localhost/~claco/BurningPlate/</kbd> with my browser we get:</p>
<p><a href="cakephp.png"><br />
<img src="cakephp.png" alt="cakephp run" width="204" height="152" /><br />
</a></p>
<h2><a name="catalyst"></a>Catalyst</h2>
<h3>Creating the Application</h3>
<p>Just like CakePHP, Catalyst also has a command line utility for creating a new application. Let&#8217;s see what what it has to say:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst $ catalyst.pl
Usage:
catalyst.pl [options] application-name

'catalyst.pl' creates a skeleton for a new application, and allows you
to upgrade the skeleton of your old application.

Options:
-force      don't create a .new file where a file to be created exists
-help       display this help and exit
-makefile   only update Makefile.PL
-scripts    only update helper scripts
-short      use short names, M/V/C instead of Model/View/Controller.

application-name must be a valid Perl module name and can include "::",
which will be converted to '-' in the project name.

Examples:
catalyst.pl My::App
catalyst.pl MyApp

To upgrade your app to a new version of Catalyst:
catalyst.pl -force -scripts MyApp</pre>
<p>Looks like we just need an application name:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst $ catalyst.pl BurningPlate
created "BurningPlate"
created "BurningPlate/script"
created "BurningPlate/lib"
created "BurningPlate/root"
created "BurningPlate/root/static"
created "BurningPlate/root/static/images"
created "BurningPlate/t"
created "BurningPlate/lib/BurningPlate"
created "BurningPlate/lib/BurningPlate/Model"
created "BurningPlate/lib/BurningPlate/View"
created "BurningPlate/lib/BurningPlate/Controller"
created "BurningPlate/burningplate.conf"
created "BurningPlate/lib/BurningPlate.pm"
created "BurningPlate/lib/BurningPlate/Controller/Root.pm"
created "BurningPlate/README"
created "BurningPlate/Changes"
created "BurningPlate/t/01app.t"
created "BurningPlate/t/02pod.t"
created "BurningPlate/t/03podcoverage.t"
created "BurningPlate/root/static/images/catalyst_logo.png"
created "BurningPlate/root/static/images/btn_120x50_built.png"
created "BurningPlate/root/static/images/btn_120x50_built_shadow.png"
created "BurningPlate/root/static/images/btn_120x50_powered.png"
created "BurningPlate/root/static/images/btn_120x50_powered_shadow.png"
created "BurningPlate/root/static/images/btn_88x31_built.png"
created "BurningPlate/root/static/images/btn_88x31_built_shadow.png"
created "BurningPlate/root/static/images/btn_88x31_powered.png"
created "BurningPlate/root/static/images/btn_88x31_powered_shadow.png"
created "BurningPlate/root/favicon.ico"
created "BurningPlate/Makefile.PL"
created "BurningPlate/script/burningplate_cgi.pl"
created "BurningPlate/script/burningplate_fastcgi.pl"
created "BurningPlate/script/burningplate_server.pl"
created "BurningPlate/script/burningplate_test.pl"
created "BurningPlate/script/burningplate_create.pl"</pre>
<h3>Running the application</h3>
<p>Unlike CakePHP, Catalyst includes a development server to get you started. No need to futz with Apache:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/catalyst $ BurningPlate/script/burningplate_server.pl
[debug] Debug messages enabled
[debug] Statistics enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
| Catalyst::Plugin::ConfigLoader  0.20                                       |
| Catalyst::Plugin::Static::Simple  0.20                                     |
'----------------------------------------------------------------------------'

[debug] Loaded dispatcher "Catalyst::Dispatcher"
[debug] Loaded engine "Catalyst::Engine::HTTP"
[debug] Found home "/Users/claco/mvc-marathon/catalyst/BurningPlate"
[debug] Loaded Config "/Users/claco/mvc-marathon/catalyst/BurningPlate/burningplate.conf"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| BurningPlate::Controller::Root                                  | instance |
'-----------------------------------------------------------------+----------'

[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private              | Class                                | Method       |
+----------------------+--------------------------------------+--------------+
| /default             | BurningPlate::Controller::Root       | default      |
| /end                 | BurningPlate::Controller::Root       | end          |
| /index               | BurningPlate::Controller::Root       | index        |
'----------------------+--------------------------------------+--------------'

[debug] Loaded Path actions:
.-------------------------------------+--------------------------------------.
| Path                                | Private                              |
+-------------------------------------+--------------------------------------+
| /                                   | /default                             |
| /                                   | /index                               |
'-------------------------------------+--------------------------------------'

[info] BurningPlate powered by Catalyst 5.7014
You can connect to your server at http://localhost:3000
[info] *** Request 1 (0.125/s) [1941] [Sat Jun 28 19:24:35 2008] ***
[debug] "GET" request for "/" from "127.0.0.1"
[info] Request took 0.017728s (56.408/s)
.----------------------------------------------------------------+-----------.
| Action                                                         | Time      |
+----------------------------------------------------------------+-----------+
| /index                                                         | 0.000507s |
| /end                                                           | 0.000745s |
'----------------------------------------------------------------+-----------'</pre>
<p>The development server was nice enough to tell us where to go. Hitting<kbd> http://localhost:3000</kbd> in our browser yields:</p>
<p><a href="catalyst.png"><br />
<img src="catalyst.png" alt="catalyst run" width="204" height="152" /><br />
</a></p>
<h2><a name="django"></a>Django</h2>
<h3>Creating the application</h3>
<p>Rinse. Lather. Repeat. As with the previous two frameworks, we just need to run a command line script to create a new application.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/django $ django-admin.py
Usage: django-admin.py action [options]
actions:
adminindex [appname ...]
Prints the admin-index template snippet for the given app name(s).

createcachetable [tablename]
Creates the table needed to use the SQL cache backend

dbshell
Runs the command-line client for the current DATABASE_ENGINE.

diffsettings
Displays differences between the current settings.py and Django's
default settings. Settings that don't appear in the defaults are
followed by "###".

dumpdata [--format][appname ...]
Output the contents of the database as a fixture of the given
format

flush [--verbosity] [--interactive]
Executes ``sqlflush`` on the current database.

inspectdb
Introspects the database tables in the given database and outputs
a Django model module.

loaddata [--verbosity] fixture, fixture, ...
Installs the named fixture(s) in the database

reset [--interactive][appname ...]
Executes ``sqlreset`` for the given app(s) in the current
database.

runfcgi [various KEY=val options, use `runfcgi help` for help]
Runs this project as a FastCGI application. Requires flup.

runserver [--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port]
Starts a lightweight Web server for development.

shell [--plain]
Runs a Python interactive interpreter. Tries to use IPython, if
it's available.

sql [appname ...]
Prints the CREATE TABLE SQL statements for the given app name(s).

sqlall [appname ...]
Prints the CREATE TABLE, initial-data and CREATE INDEX SQL
statements for the given model module name(s).

sqlclear [appname ...]
Prints the DROP TABLE SQL statements for the given app name(s).

sqlcustom [appname ...]
Prints the custom table modifying SQL statements for the given app
name(s).

sqlflush
Returns a list of the SQL statements required to return all tables
in the database to the state they were in just after they were
installed.

sqlindexes [appname ...]
Prints the CREATE INDEX SQL statements for the given model module
name(s).

sqlinitialdata
RENAMED: see 'sqlcustom'

sqlreset [appname ...]
Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the
given app name(s).

sqlsequencereset [appname ...]
Prints the SQL statements for resetting PostgreSQL sequences for
the given app name(s).

startapp [appname]
Creates a Django app directory structure for the given app name in
the current directory.

startproject [projectname]
Creates a Django project directory structure for the given project
name in the current directory.

syncdb [--verbosity] [--interactive]
Create the database tables for all apps in INSTALLED_APPS whose
tables haven't already been created.

test [--verbosity] [appname ...]
Runs the test suite for the specified applications, or the entire
site if no apps are specified

validate
Validates all installed models.

Options:
--version             show program's version number and exit
-h, --help            show this help message and exit
--settings=SETTINGS   Python path to settings module, e.g.
"myproject.settings.main". If this isn't provided, the
DJANGO_SETTINGS_MODULE environment variable will be
used.
--pythonpath=PYTHONPATH
Lets you manually add a directory the Python path,
e.g. "/home/djangoprojects/myproject".
--plain               Tells Django to use plain Python, not IPython, for
"shell" command.
--noinput             Tells Django to NOT prompt the user for input of any
kind.
--noreload            Tells Django to NOT use the auto-reloader when running
the development server.
--format=FORMAT       Specifies the output serialization format for fixtures
--indent=INDENT       Specifies the indent level to use when pretty-printing
output
--verbosity=VERBOSITY
Verbosity level; 0=minimal output, 1=normal output,
2=all output
--adminmedia=ADMIN_MEDIA_PATH
Specifies the directory from which to serve admin
media for runserver.</pre>
<p>To create a new app, we&#8217;ll run the <kbd>startproject</kbd> command:</p>
<pre>claco@mbp ~/mvc-marathon/django $ django-admin.py startproject BurningPlate</pre>
<p>Unfortunately, no matter what &#8211;verbosity level I set, this is the only output I get.</p>
<h3>Running the Application</h3>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/django $ python BurningPlate/manage.py runserver
Validating models...
0 errors found.

Django version 0.96.2, using settings 'BurningPlate.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[28/Jun/2008 18:40:25] "GET / HTTP/1.1" 404 2065</pre>
<p>Lets hit <kbd>http://127.0.0.1:8000/</kbd> and see what we have:</p>
<p><a href="django.png"><br />
<img src="django.png" alt="django run" width="204" height="152" /><br />
</a></p>
<h2><a name="rails"></a>Ruby on Rails</h2>
<h3>Creating the Application</h3>
<p>Another framework; another script.</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails $ rails
Usage: /sw/bin/rails /path/to/your/app [options]

Options:
-r, --ruby=path                  Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).
Default: /sw/bin/ruby1.8
-d, --database=name              Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3).
Default: mysql
-f, --freeze                     Freeze Rails in vendor/rails from the gems generating the skeleton
Default: false

Rails Info:
-v, --version                    Show the Rails version number and quit.
-h, --help                       Show this help message and quit.

General Options:
-p, --pretend                    Run but do not make any changes.
--force                      Overwrite files that already exist.
-s, --skip                       Skip files that already exist.
-q, --quiet                      Suppress normal output.
-t, --backtrace                  Debugging: show backtrace on errors.
-c, --svn                        Modify files with subversion. (Note: svn must be in path)

Description:
The 'rails' command creates a new Rails application with a default
directory structure and configuration at the path you specify.

Example:
rails ~/Code/Ruby/weblog

This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
See the README in the newly created application to get going.</pre>
<p>Looks like Catalyst. Give it an app name and go:</p>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails $ rails BurningPlate
create
create  app/controllers
create  app/helpers
create  app/models
create  app/views/layouts
create  config/environments
create  config/initializers
create  db
create  doc
create  lib
create  lib/tasks
create  log
create  public/images
create  public/javascripts
create  public/stylesheets
create  script/performance
create  script/process
create  test/fixtures
create  test/functional
create  test/integration
create  test/mocks/development
create  test/mocks/test
create  test/unit
create  vendor
create  vendor/plugins
create  tmp/sessions
create  tmp/sockets
create  tmp/cache
create  tmp/pids
create  Rakefile
create  README
create  app/controllers/application.rb
create  app/helpers/application_helper.rb
create  test/test_helper.rb
create  config/database.yml
create  config/routes.rb
create  public/.htaccess
create  config/initializers/inflections.rb
create  config/initializers/mime_types.rb
create  config/boot.rb
create  config/environment.rb
create  config/environments/production.rb
create  config/environments/development.rb
create  config/environments/test.rb
create  script/about
create  script/console
create  script/destroy
create  script/generate
create  script/performance/benchmarker
create  script/performance/profiler
create  script/performance/request
create  script/process/reaper
create  script/process/spawner
create  script/process/inspector
create  script/runner
create  script/server
create  script/plugin
create  public/dispatch.rb
create  public/dispatch.cgi
create  public/dispatch.fcgi
create  public/404.html
create  public/422.html
create  public/500.html
create  public/index.html
create  public/favicon.ico
create  public/robots.txt
create  public/images/rails.png
create  public/javascripts/prototype.js
create  public/javascripts/effects.js
create  public/javascripts/dragdrop.js
create  public/javascripts/controls.js
create  public/javascripts/application.js
create  doc/README_FOR_APP
create  log/server.log
create  log/production.log
create  log/development.log
create  log/test.log</pre>
<h3>Running the Application</h3>
<pre class="brush:shell">claco@mbp ~/mvc-marathon/rails $ BurningPlate/script/server
=&gt; Booting WEBrick...
=&gt; Rails application started on http://0.0.0.0:3000
=&gt; Ctrl-C to shutdown server; call with --help for options
[2008-06-28 19:20:36] INFO  WEBrick 1.3.1
[2008-06-28 19:20:36] INFO  ruby 1.8.6 (2008-03-03) [i686-darwin]
[2008-06-28 19:20:36] INFO  WEBrick::HTTPServer#start: pid=1929 port=3000
127.0.0.1 - - [28/Jun/2008:19:20:41 EDT] "GET / HTTP/1.1" 200 7557
- -&gt; /
127.0.0.1 - - [28/Jun/2008:19:20:41 EDT] "GET /javascripts/prototype.js HTTP/1.1" 200 125605
http://localhost:3000/ -&gt; /javascripts/prototype.js
127.0.0.1 - - [28/Jun/2008:19:20:41 EDT] "GET /javascripts/effects.js HTTP/1.1" 200 38916
http://localhost:3000/ -&gt; /javascripts/effects.js
127.0.0.1 - - [28/Jun/2008:19:20:41 EDT] "GET /images/rails.png HTTP/1.1" 200 1787
http://localhost:3000/ -&gt; /images/rails.png</pre>
<p>Loading <kbd>http://0.0.0.0:3000</kbd> in our browser, we see:</p>
<p><a href="rails.png"><br />
<img src="rails.png" alt="rails run" width="204" height="152" /><br />
</a></p>
<h2><a name="conclusions"></a>Conclusions</h2>
<p>There weren&#8217;t too many shockers here. For the most part, it&#8217;s just a matter of running a command to get an application structure up and running. There are some areas that could use a few tweaks to help out the people new to their respective frameworks:</p>
<ul>
<li> CakePHP really could use a development server. For development, it&#8217;s nice to not have to mess with Apache configs.</li>
<li> Django needs to output something when it&#8217;s successfully created a new application. No output equates to thinking nothing happened or that something is broken.</li>
<li> Catalyst, Django and Rails would be a touch better if after they created the new applications would tell the user how to run them. i.e. &#8220;App created. please run xxx xxxx/xxxxx ro start your new app&#8221;.</li>
<li> All of the frameworks except for ASP.NET MVC tell the user where to go next and/or what files to start to configure. It would be nice if ASP.NET MVC did the ame thing rather than just display the &#8220;Home&#8221; page.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/mvc-marathon-part-1-creating-a-new-application/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MVC Marathon</title>
		<link>http://chrislaco.com/articles/mvc-marathon/</link>
		<comments>http://chrislaco.com/articles/mvc-marathon/#comments</comments>
		<pubDate>Sun, 29 Jun 2008 05:42:17 +0000</pubDate>
		<dc:creator>claco</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[catalyst]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[merb]]></category>
		<category><![CDATA[mojo]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://chrislaco.com/?p=4</guid>
		<description><![CDATA[Over the last few years, I&#8217;ve spent a fair amount of time on the same projects, using the same tools, in the same languages. By day, I&#8217;m a mild mannered .NET programmer. By night, I fly the Perl flag, and &#8230; <a href="http://chrislaco.com/articles/mvc-marathon/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Over the last few years, I&#8217;ve spent a fair amount of time on the same projects, using the same tools, in the same languages. By day, I&#8217;m a mild mannered .NET programmer. By night, I fly the Perl flag, and spend a lot of time using Catalyst, one of the Perl based MVC frameworks. While I&#8217;m not abandoning those technologies, I think it&#8217;s time for something new.</p>
<p><span id="more-4"></span></p>
<p>They say you should learn a new programming language every year or so, and I&#8217;m long over due. Why not learn three languages at the same time to make up for lost time? <img src='http://chrislaco.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<h2>The Challenge</h2>
<p>The challenge is to build the same mvc application on all of the frameworks listed below. While I&#8217;m comfortable with Catalyst/Perl and ASP.NET, I&#8217;d like to be able to work on any project that comes along rather than simply answering &#8220;Sorry, I can&#8217;t help. I don&#8217;t program in that frameowork/language.&#8221; I started my career as a Windows-only web programmer and over time, learned my way through FreeBSD, Apache, Perl and Catalyst.</p>
<p>Will I be an &#8216;expert&#8217; in Ruby or Python when I&#8217;m done? Surely not. But 3 years ago, I couldn&#8217;t program in Perl either and 10 years ago, I could&#8217;t even begin to get a *nix OS installed and configured to run Apache.</p>
<h2>The Frameworks</h2>
<p>For this adventure, I&#8217;ve picked what might be considered the most popular mvc framework for each language. These frameworks will be:</p>
<h3><a href="http://www.asp.net/mvc/">ASP.NET MVC: Preview 3</a></h3>
<p>While my day job includes programming in ASP.NET 2.0 WebForms and I have followed the MVC progress, I have not yet created an ASP.NET MVC app. Just to be a little more challenging, I will be doing this project in C# instead of the usual VB.NET that we use at work.</p>
<h3><a href="http://www.cakephp.org/">CakePHP: 1.2RC2</a></h3>
<p>I&#8217;ve read as much of the manual as I can at this point. This will be my first CakePHP application and my first PHP programming since PHP4 was released.</p>
<h3><a href="http://www.catalystframework.org/">Catalyst: 5.7</a></h3>
<p>Catalyst is one of the many MVC frameworks written in Perl. I&#8217;ve been using Perl and Catalyst for about 3 years. While this could be my strongest framework, I&#8217;ll be using none of my existing bag of tricks.</p>
<h3><a href="http://www.djangoproject.com/">Django: 0.96</a></h3>
<p>I&#8217;ve never written a single line of Python before. After looking at the Python and Ruby syntax on multiple occasions in the past, Python seems to be the language furthest from how my brain works now compared to Perl/C#.</p>
<h3><a href="http://www.rubyonrails.org/">Ruby on Rails: 2.1</a></h3>
<p>Just like Django/Python, I&#8217;ve never written a line if Ruby in my life. I&#8217;ve been through the screen casts and sifted around a few articles and book excerpts. However, unlike Python, Ruby seems to fit how my brain works with Perl a littler cleaner.</p>
<h2>The Application</h2>
<p>I&#8217;m sick of the usual blog demo applications so I&#8217;m going to go with something a little closer to my heart. Those who know me know that I&#8217;m a big fan of hot food: If you don&#8217;t sweat, it&#8217;s not hot enough. So for this challenge, I&#8217;m going to build a site that tracks lists of restaurants and a list of menu items for each eatery hot dish.</p>
<p>The application will be built in stages. Each stage will be built in every framework before moving on to the next step. After completing each stage, I&#8217;ll post a new blog entry covering what code needed to be created in each framework and the pros and cons each framework provides. You can follow the source code progress in my GitHub repository:</p>
<p><a href="http://github.com/claco/mvc-marathon/">http://github.com/claco/mvc-marathon/<br />
</a></p>
<h2>Step by Step</h2>
<ol>
<li><a href="/articles/mvc-marathon-part-1-creating-a-new-application/">Part 1. Creating a New Application</a></li>
<li><a href="/articles/mvc-marathon-part-2-creating-a-database-and-model/">Part 2. Creating a Database and Model</a></li>
<li><a href="/articles/mvc-marathon-part-3-creating-a-restaurants-controller-and-view/">Part 3. Creating a Restaurants Controller and View</a></li>
<li>&#8230;</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://chrislaco.com/articles/mvc-marathon/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
